Merge pull request #699 from eikek/small-improvements

Small improvements
This commit is contained in:
mergify[bot] 2021-03-10 23:13:27 +00:00 committed by GitHub
commit 2e9b948d37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 700 additions and 243 deletions

View File

@ -45,12 +45,6 @@ val sharedSettings = Seq(
(scalacOptions.value.filter(o => !o.contains("-Xlint") && !o.contains("-W"))) (scalacOptions.value.filter(o => !o.contains("-Xlint") && !o.contains("-W")))
) ++ scalafixSettings ) ++ scalafixSettings
val testSettings = Seq(
testFrameworks += new TestFramework("minitest.runner.Framework"),
libraryDependencies ++= Dependencies.miniTest ++ Dependencies.logging.map(_ % Test),
Test / fork := true
)
val testSettingsMUnit = Seq( val testSettingsMUnit = Seq(
libraryDependencies ++= Dependencies.munit.map(_ % Test), libraryDependencies ++= Dependencies.munit.map(_ % Test),
testFrameworks += new TestFramework("munit.Framework") testFrameworks += new TestFramework("munit.Framework")
@ -219,6 +213,12 @@ val openapiScalaSettings = Seq(
case "personuse" => case "personuse" =>
field => field =>
field.copy(typeDef = TypeDef("PersonUse", Imports("docspell.common.PersonUse"))) field.copy(typeDef = TypeDef("PersonUse", Imports("docspell.common.PersonUse")))
case "orguse" =>
field =>
field.copy(typeDef = TypeDef("OrgUse", Imports("docspell.common.OrgUse")))
case "equipmentuse" =>
field =>
field.copy(typeDef = TypeDef("EquipmentUse", Imports("docspell.common.EquipmentUse")))
})) }))
) )
@ -231,7 +231,7 @@ val common = project
.in(file("modules/common")) .in(file("modules/common"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-common", name := "docspell-common",
libraryDependencies ++= libraryDependencies ++=
@ -249,7 +249,7 @@ val files = project
.in(file("modules/files")) .in(file("modules/files"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-files", name := "docspell-files",
libraryDependencies ++= libraryDependencies ++=
@ -308,7 +308,7 @@ val store = project
.in(file("modules/store")) .in(file("modules/store"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-store", name := "docspell-store",
libraryDependencies ++= libraryDependencies ++=
@ -330,7 +330,7 @@ val extract = project
.in(file("modules/extract")) .in(file("modules/extract"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-extract", name := "docspell-extract",
libraryDependencies ++= libraryDependencies ++=
@ -347,7 +347,7 @@ val convert = project
.in(file("modules/convert")) .in(file("modules/convert"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-convert", name := "docspell-convert",
libraryDependencies ++= libraryDependencies ++=
@ -361,7 +361,7 @@ val analysis = project
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.enablePlugins(NerModelsPlugin) .enablePlugins(NerModelsPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings(NerModelsPlugin.nerClassifierSettings) .settings(NerModelsPlugin.nerClassifierSettings)
.settings( .settings(
name := "docspell-analysis", name := "docspell-analysis",
@ -375,7 +375,7 @@ val ftsclient = project
.in(file("modules/fts-client")) .in(file("modules/fts-client"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-fts-client", name := "docspell-fts-client",
libraryDependencies ++= Seq.empty libraryDependencies ++= Seq.empty
@ -386,7 +386,7 @@ val ftssolr = project
.in(file("modules/fts-solr")) .in(file("modules/fts-solr"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-fts-solr", name := "docspell-fts-solr",
libraryDependencies ++= libraryDependencies ++=
@ -402,7 +402,7 @@ val restapi = project
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.enablePlugins(OpenApiSchema) .enablePlugins(OpenApiSchema)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings(openapiScalaSettings) .settings(openapiScalaSettings)
.settings( .settings(
name := "docspell-restapi", name := "docspell-restapi",
@ -420,7 +420,7 @@ val joexapi = project
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.enablePlugins(OpenApiSchema) .enablePlugins(OpenApiSchema)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings(openapiScalaSettings) .settings(openapiScalaSettings)
.settings( .settings(
name := "docspell-joexapi", name := "docspell-joexapi",
@ -438,7 +438,7 @@ val backend = project
.in(file("modules/backend")) .in(file("modules/backend"))
.disablePlugins(RevolverPlugin) .disablePlugins(RevolverPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings( .settings(
name := "docspell-backend", name := "docspell-backend",
libraryDependencies ++= libraryDependencies ++=
@ -473,7 +473,7 @@ val joex = project
.in(file("modules/joex")) .in(file("modules/joex"))
.enablePlugins(BuildInfoPlugin, JavaServerAppPackaging, DebianPlugin, SystemdPlugin) .enablePlugins(BuildInfoPlugin, JavaServerAppPackaging, DebianPlugin, SystemdPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings(debianSettings("docspell-joex")) .settings(debianSettings("docspell-joex"))
.settings(buildInfoSettings) .settings(buildInfoSettings)
.settings( .settings(
@ -512,7 +512,7 @@ val restserver = project
.in(file("modules/restserver")) .in(file("modules/restserver"))
.enablePlugins(BuildInfoPlugin, JavaServerAppPackaging, DebianPlugin, SystemdPlugin) .enablePlugins(BuildInfoPlugin, JavaServerAppPackaging, DebianPlugin, SystemdPlugin)
.settings(sharedSettings) .settings(sharedSettings)
.settings(testSettings) .settings(testSettingsMUnit)
.settings(debianSettings("docspell-server")) .settings(debianSettings("docspell-server"))
.settings(buildInfoSettings) .settings(buildInfoSettings)
.settings( .settings(
@ -740,7 +740,8 @@ def packageTools(logger: Logger, dir: File, version: String): Seq[File] = {
wx / "icons" / "logo-96.png" -> "icons/logo-96.png", wx / "icons" / "logo-96.png" -> "icons/logo-96.png",
wx / "manifest.json" -> "manifest.json" wx / "manifest.json" -> "manifest.json"
), ),
webext webext,
None
) )
val excludes = Seq(wx, target) val excludes = Seq(wx, target)
@ -757,7 +758,8 @@ def packageTools(logger: Logger, dir: File, version: String): Seq[File] = {
wx / "native/app_manifest.json" -> s"docspell-tools-${version}/firefox/native/app_manifest.json", wx / "native/app_manifest.json" -> s"docspell-tools-${version}/firefox/native/app_manifest.json",
wx / "native/native.py" -> s"docspell-tools-${version}/firefox/native/native.py" wx / "native/native.py" -> s"docspell-tools-${version}/firefox/native/native.py"
) ++ files, ) ++ files,
archive archive,
None
) )
Seq(archive) Seq(archive)

View File

@ -1,6 +1,6 @@
package docspell.analysis.classifier package docspell.analysis.classifier
import minitest._ import munit._
import cats.effect._ import cats.effect._
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
import java.nio.file.Paths import java.nio.file.Paths
@ -10,7 +10,7 @@ import fs2.Stream
import cats.data.Kleisli import cats.data.Kleisli
import TextClassifier.Data import TextClassifier.Data
object StanfordTextClassifierSuite extends SimpleTestSuite { class StanfordTextClassifierSuite extends FunSuite {
val logger = Logger.log4s[IO](org.log4s.getLogger) val logger = Logger.log4s[IO](org.log4s.getLogger)
implicit val CS = IO.contextShift(ExecutionContext.global) implicit val CS = IO.contextShift(ExecutionContext.global)

View File

@ -1,9 +1,9 @@
package docspell.analysis.contact package docspell.analysis.contact
import docspell.common.{NerLabel, NerTag} import docspell.common.{NerLabel, NerTag}
import minitest.SimpleTestSuite import munit._
object ContactAnnotateSpec extends SimpleTestSuite { class ContactAnnotateSpec extends FunSuite {
test("find email") { test("find email") {

View File

@ -1,11 +1,11 @@
package docspell.analysis.date package docspell.analysis.date
import docspell.files.TestFiles import docspell.files.TestFiles
import minitest.SimpleTestSuite import munit._
import docspell.common._ import docspell.common._
import java.time._ import java.time._
object DateFindSpec extends SimpleTestSuite { class DateFindSpec extends FunSuite {
test("find simple dates") { test("find simple dates") {
val expect = Vector( val expect = Vector(

View File

@ -2,19 +2,17 @@ package docspell.analysis.nlp
import docspell.analysis.Env import docspell.analysis.Env
import docspell.common.Language.NLPLanguage import docspell.common.Language.NLPLanguage
import minitest.SimpleTestSuite import munit._
import docspell.files.TestFiles import docspell.files.TestFiles
import docspell.common._ import docspell.common._
object BaseCRFAnnotatorSuite extends SimpleTestSuite { class BaseCRFAnnotatorSuite extends FunSuite {
def annotate(language: NLPLanguage): String => Vector[NerLabel] = def annotate(language: NLPLanguage): String => Vector[NerLabel] =
BasicCRFAnnotator.nerAnnotate(BasicCRFAnnotator.Cache.getAnnotator(language)) BasicCRFAnnotator.nerAnnotate(BasicCRFAnnotator.Cache.getAnnotator(language))
test("find english ner labels") { test("find english ner labels") {
if (Env.isCI) { assume(!Env.isCI, "Test ignored on travis.")
ignore("Test ignored on travis.")
}
val labels = annotate(Language.English)(TestFiles.letterENText) val labels = annotate(Language.English)(TestFiles.letterENText)
val expect = Vector( val expect = Vector(
@ -52,9 +50,7 @@ object BaseCRFAnnotatorSuite extends SimpleTestSuite {
} }
test("find german ner labels") { test("find german ner labels") {
if (Env.isCI) { assume(!Env.isCI, "Test ignored on travis.")
ignore("Test ignored on travis.")
}
val labels = annotate(Language.German)(TestFiles.letterDEText) val labels = annotate(Language.German)(TestFiles.letterDEText)
val expect = Vector( val expect = Vector(

View File

@ -4,22 +4,20 @@ import java.nio.file.Paths
import cats.effect.IO import cats.effect.IO
import docspell.analysis.Env import docspell.analysis.Env
import minitest.SimpleTestSuite import munit._
import docspell.files.TestFiles import docspell.files.TestFiles
import docspell.common._ import docspell.common._
import docspell.common.syntax.FileSyntax._ import docspell.common.syntax.FileSyntax._
import edu.stanford.nlp.pipeline.StanfordCoreNLP import edu.stanford.nlp.pipeline.StanfordCoreNLP
object StanfordNerAnnotatorSuite extends SimpleTestSuite { class StanfordNerAnnotatorSuite extends FunSuite {
lazy val germanClassifier = lazy val germanClassifier =
new StanfordCoreNLP(Properties.nerGerman(None, false)) new StanfordCoreNLP(Properties.nerGerman(None, false))
lazy val englishClassifier = lazy val englishClassifier =
new StanfordCoreNLP(Properties.nerEnglish(None)) new StanfordCoreNLP(Properties.nerEnglish(None))
test("find english ner labels") { test("find english ner labels") {
if (Env.isCI) { assume(!Env.isCI, "Test ignored on travis.")
ignore("Test ignored on travis.")
}
val labels = val labels =
StanfordNerAnnotator.nerAnnotate(englishClassifier, TestFiles.letterENText) StanfordNerAnnotator.nerAnnotate(englishClassifier, TestFiles.letterENText)
@ -58,9 +56,7 @@ object StanfordNerAnnotatorSuite extends SimpleTestSuite {
} }
test("find german ner labels") { test("find german ner labels") {
if (Env.isCI) { assume(!Env.isCI, "Test ignored on travis.")
ignore("Test ignored on travis.")
}
val labels = val labels =
StanfordNerAnnotator.nerAnnotate(germanClassifier, TestFiles.letterDEText) StanfordNerAnnotator.nerAnnotate(germanClassifier, TestFiles.letterDEText)
@ -83,9 +79,7 @@ object StanfordNerAnnotatorSuite extends SimpleTestSuite {
} }
test("regexner-only annotator") { test("regexner-only annotator") {
if (Env.isCI) { assume(!Env.isCI, "Test ignored on travis.")
ignore("Test ignored on travis.")
}
val regexNerContent = val regexNerContent =
s"""(?i)volantino ag${"\t"}ORGANIZATION${"\t"}LOCATION,PERSON,MISC${"\t"}3 s"""(?i)volantino ag${"\t"}ORGANIZATION${"\t"}LOCATION,PERSON,MISC${"\t"}3

View File

@ -1,8 +1,8 @@
package docspell.analysis.split package docspell.analysis.split
import minitest.SimpleTestSuite import munit._
object TestSplitterSpec extends SimpleTestSuite { class TestSplitterSpec extends FunSuite {
test("simple splitting") { test("simple splitting") {
val text = """hiermit kündige ich meine Mitgliedschaft in der Kranken- und val text = """hiermit kündige ich meine Mitgliedschaft in der Kranken- und

View File

@ -0,0 +1,46 @@
package docspell.common
import cats.data.NonEmptyList
import io.circe.Decoder
import io.circe.Encoder
sealed trait EquipmentUse { self: Product =>
final def name: String =
self.productPrefix.toLowerCase()
}
object EquipmentUse {
case object Concerning extends EquipmentUse
case object Disabled extends EquipmentUse
def concerning: EquipmentUse = Concerning
def disabled: EquipmentUse = Disabled
val all: NonEmptyList[EquipmentUse] =
NonEmptyList.of(concerning, disabled)
val notDisabled: NonEmptyList[EquipmentUse] =
NonEmptyList.of(concerning)
def fromString(str: String): Either[String, EquipmentUse] =
str.toLowerCase() match {
case "concerning" =>
Right(Concerning)
case "disabled" =>
Right(Disabled)
case _ =>
Left(s"Unknown equipment-use: $str")
}
def unsafeFromString(str: String): EquipmentUse =
fromString(str).fold(sys.error, identity)
implicit val jsonDecoder: Decoder[EquipmentUse] =
Decoder.decodeString.emap(fromString)
implicit val jsonEncoder: Encoder[EquipmentUse] =
Encoder.encodeString.contramap(_.name)
}

View File

@ -26,7 +26,7 @@ object Ident {
implicit val identEq: Eq[Ident] = implicit val identEq: Eq[Ident] =
Eq.by(_.id) Eq.by(_.id)
val chars: Set[Char] = (('A' to 'Z') ++ ('a' to 'z') ++ ('0' to '9') ++ "-_.").toSet val chars: Set[Char] = (('A' to 'Z') ++ ('a' to 'z') ++ ('0' to '9') ++ "-_.@").toSet
def randomUUID[F[_]: Sync]: F[Ident] = def randomUUID[F[_]: Sync]: F[Ident] =
Sync[F].delay(unsafe(UUID.randomUUID.toString)) Sync[F].delay(unsafe(UUID.randomUUID.toString))

View File

@ -0,0 +1,46 @@
package docspell.common
import cats.data.NonEmptyList
import io.circe.Decoder
import io.circe.Encoder
sealed trait OrgUse { self: Product =>
final def name: String =
self.productPrefix.toLowerCase()
}
object OrgUse {
case object Correspondent extends OrgUse
case object Disabled extends OrgUse
def correspondent: OrgUse = Correspondent
def disabled: OrgUse = Disabled
val all: NonEmptyList[OrgUse] =
NonEmptyList.of(correspondent, disabled)
val notDisabled: NonEmptyList[OrgUse] =
NonEmptyList.of(correspondent)
def fromString(str: String): Either[String, OrgUse] =
str.toLowerCase() match {
case "correspondent" =>
Right(Correspondent)
case "disabled" =>
Right(Disabled)
case _ =>
Left(s"Unknown organization-use: $str")
}
def unsafeFromString(str: String): OrgUse =
fromString(str).fold(sys.error, identity)
implicit val jsonDecoder: Decoder[OrgUse] =
Decoder.decodeString.emap(fromString)
implicit val jsonEncoder: Encoder[OrgUse] =
Encoder.encodeString.contramap(_.name)
}

View File

@ -16,6 +16,7 @@ object PersonUse {
case object Correspondent extends PersonUse case object Correspondent extends PersonUse
case object Concerning extends PersonUse case object Concerning extends PersonUse
case object Both extends PersonUse case object Both extends PersonUse
case object Disabled extends PersonUse
def concerning: PersonUse = Concerning def concerning: PersonUse = Concerning
def correspondent: PersonUse = Correspondent def correspondent: PersonUse = Correspondent
@ -35,6 +36,8 @@ object PersonUse {
Right(Concerning) Right(Concerning)
case "both" => case "both" =>
Right(Both) Right(Both)
case "disabled" =>
Right(Disabled)
case _ => case _ =>
Left(s"Unknown person-use: $str") Left(s"Unknown person-use: $str")
} }

View File

@ -1,8 +1,8 @@
package docspell.common package docspell.common
import minitest._ import munit._
object FileNameTest extends SimpleTestSuite { class FileNameTest extends FunSuite {
test("make filename") { test("make filename") {
val data = List( val data = List(

View File

@ -1,9 +1,9 @@
package docspell.common package docspell.common
import minitest._ import munit._
import Glob._ import Glob._
object GlobTest extends SimpleTestSuite { class GlobTest extends FunSuite {
test("literals") { test("literals") {
assert(Glob.pattern(Pattern(Segment(Token.Literal("hello")))).matches(true)("hello")) assert(Glob.pattern(Pattern(Segment(Token.Literal("hello")))).matches(true)("hello"))

View File

@ -1,10 +1,10 @@
package docspell.common package docspell.common
import minitest._ import munit._
import cats.data.NonEmptyList import cats.data.NonEmptyList
import docspell.common.MetaProposal.Candidate import docspell.common.MetaProposal.Candidate
object MetaProposalListTest extends SimpleTestSuite { class MetaProposalListTest extends FunSuite {
test("flatten retains order of candidates") { test("flatten retains order of candidates") {
val cand1 = Candidate(IdRef(Ident.unsafe("123"), "name"), Set.empty) val cand1 = Candidate(IdRef(Ident.unsafe("123"), "name"), Set.empty)

View File

@ -1,8 +1,8 @@
package docspell.common package docspell.common
import minitest._ import munit._
object NerLabelSpanTest extends SimpleTestSuite { class NerLabelSpanTest extends FunSuite {
test("build") { test("build") {
val labels = List( val labels = List(

View File

@ -11,10 +11,10 @@ import docspell.convert.ConversionResult.Handler
import docspell.convert.extern.{TesseractConfig, UnoconvConfig, WkHtmlPdfConfig} import docspell.convert.extern.{TesseractConfig, UnoconvConfig, WkHtmlPdfConfig}
import docspell.convert.flexmark.MarkdownConfig import docspell.convert.flexmark.MarkdownConfig
import docspell.files.{ExampleFiles, TestFiles} import docspell.files.{ExampleFiles, TestFiles}
import minitest.SimpleTestSuite import munit._
import docspell.convert.extern.OcrMyPdfConfig import docspell.convert.extern.OcrMyPdfConfig
object ConversionTest extends SimpleTestSuite with FileChecks { class ConversionTest extends FunSuite with FileChecks {
val blocker = TestFiles.blocker val blocker = TestFiles.blocker
implicit val CS = TestFiles.CS implicit val CS = TestFiles.CS
@ -101,59 +101,58 @@ object ConversionTest extends SimpleTestSuite with FileChecks {
) )
test("convert to pdf") { test("convert to pdf") {
if (!commandsExist) ignore("At least one of the conversion programs not found") assume(commandsExist, "At least one of the conversion programs not found")
else
File
.withTempDir[IO](target, "convpdf")
.use { dir =>
conversion.use { conv =>
def check(n: Long): Handler[IO, Unit] =
storePdfHandler(dir.resolve(s"test-$n.pdf")).map { p =>
assert(p.isNonEmpty && p.isPDF)
}
runConversion(pdfOnly, check, conv).compile.drain File
} .withTempDir[IO](target, "convpdf")
.use { dir =>
conversion.use { conv =>
def check(n: Long): Handler[IO, Unit] =
storePdfHandler(dir.resolve(s"test-$n.pdf")).map { p =>
assert(p.isNonEmpty && p.isPDF)
}
runConversion(pdfOnly, check, conv).compile.drain
} }
.unsafeRunSync() }
.unsafeRunSync()
} }
test("convert image to pdf and txt") { test("convert image to pdf and txt") {
if (!commandsExist) ignore("At least one of the conversion programs not found") assume(commandsExist, "At least one of the conversion programs not found")
else File
File .withTempDir[IO](target, "convimgpdf")
.withTempDir[IO](target, "convimgpdf") .use { dir =>
.use { dir => conversion.use { conv =>
conversion.use { conv => def check(n: Long): Handler[IO, Unit] =
def check(n: Long): Handler[IO, Unit] = storePdfTxtHandler(dir.resolve(s"test-$n.pdf"), dir.resolve(s"test-$n.txt"))
storePdfTxtHandler(dir.resolve(s"test-$n.pdf"), dir.resolve(s"test-$n.txt")) .map { case (p, t) =>
.map { case (p, t) => assert(p.isNonEmpty && p.isPDF)
assert(p.isNonEmpty && p.isPDF) assert(t.isNonEmpty && t.isPlainText)
assert(t.isNonEmpty && t.isPlainText) }
}
runConversion(pdfAndTxt, check, conv).compile.drain runConversion(pdfAndTxt, check, conv).compile.drain
}
} }
.unsafeRunSync() }
.unsafeRunSync()
} }
test("do not convert image bombs") { test("do not convert image bombs") {
if (!commandsExist) ignore("At least one of the conversion programs not found") assume(commandsExist, "At least one of the conversion programs not found")
else
conversion
.use { conv =>
def check: Handler[IO, Unit] =
Kleisli({
case ConversionResult.InputMalformed(_, _) =>
().pure[IO]
case cr =>
IO.raiseError(new Exception(s"Unexpected result: $cr"))
})
runConversion(bombs, _ => check, conv).compile.drain conversion
} .use { conv =>
.unsafeRunSync() def check: Handler[IO, Unit] =
Kleisli({
case ConversionResult.InputMalformed(_, _) =>
().pure[IO]
case cr =>
IO.raiseError(new Exception(s"Unexpected result: $cr"))
})
runConversion(bombs, _ => check, conv).compile.drain
}
.unsafeRunSync()
} }
def runConversion[A]( def runConversion[A](

View File

@ -6,10 +6,10 @@ import cats.effect._
import docspell.common._ import docspell.common._
import docspell.convert._ import docspell.convert._
import docspell.files.{ExampleFiles, TestFiles} import docspell.files.{ExampleFiles, TestFiles}
import minitest.SimpleTestSuite import munit._
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
object ExternConvTest extends SimpleTestSuite with FileChecks { class ExternConvTest extends FunSuite with FileChecks {
val blocker = TestFiles.blocker val blocker = TestFiles.blocker
implicit val CS = TestFiles.CS implicit val CS = TestFiles.CS
val utf8 = StandardCharsets.UTF_8 val utf8 = StandardCharsets.UTF_8
@ -22,26 +22,24 @@ object ExternConvTest extends SimpleTestSuite with FileChecks {
Seq("-s", "A4", "--encoding", "UTF-8", "-", "{{outfile}}"), Seq("-s", "A4", "--encoding", "UTF-8", "-", "{{outfile}}"),
Duration.seconds(20) Duration.seconds(20)
) )
assume(commandExists(cfg.program), s"Command ${cfg.program} not found. Ignore tests.")
File
.withTempDir[IO](target, "wkhtmltopdf")
.use(dir =>
IO {
val wkCfg = WkHtmlPdfConfig(cfg, target)
val p =
WkHtmlPdf
.toPDF[IO, Path](wkCfg, 8192, utf8, SanitizeHtml.none, blocker, logger)(
ExampleFiles.letter_de_html.readURL[IO](8192, blocker),
storePdfHandler(dir.resolve("test.pdf"))
)
.unsafeRunSync()
if (!commandExists(cfg.program)) ignore(s"Command ${cfg.program} not found") assert(p.isNonEmpty && p.isPDF)
else }
File )
.withTempDir[IO](target, "wkhtmltopdf") .unsafeRunSync()
.use(dir =>
IO {
val wkCfg = WkHtmlPdfConfig(cfg, target)
val p =
WkHtmlPdf
.toPDF[IO, Path](wkCfg, 8192, utf8, SanitizeHtml.none, blocker, logger)(
ExampleFiles.letter_de_html.readURL[IO](8192, blocker),
storePdfHandler(dir.resolve("test.pdf"))
)
.unsafeRunSync()
assert(p.isNonEmpty && p.isPDF)
}
)
.unsafeRunSync()
} }
test("convert office to pdf") { test("convert office to pdf") {
@ -51,25 +49,24 @@ object ExternConvTest extends SimpleTestSuite with FileChecks {
Duration.seconds(20) Duration.seconds(20)
) )
if (!commandExists(cfg.program)) ignore(s"Command ${cfg.program} not found") assume(commandExists(cfg.program), s"Command ${cfg.program} not found. Ignore tests.")
else File
File .withTempDir[IO](target, "unoconv")
.withTempDir[IO](target, "unoconv") .use(dir =>
.use(dir => IO {
IO { val ucCfg = UnoconvConfig(cfg, target)
val ucCfg = UnoconvConfig(cfg, target) val p =
val p = Unoconv
Unoconv .toPDF[IO, Path](ucCfg, 8192, blocker, logger)(
.toPDF[IO, Path](ucCfg, 8192, blocker, logger)( ExampleFiles.examples_sample_docx.readURL[IO](8192, blocker),
ExampleFiles.examples_sample_docx.readURL[IO](8192, blocker), storePdfHandler(dir.resolve("test.pdf"))
storePdfHandler(dir.resolve("test.pdf")) )
) .unsafeRunSync()
.unsafeRunSync()
assert(p.isNonEmpty && p.isPDF) assert(p.isNonEmpty && p.isPDF)
} }
) )
.unsafeRunSync() .unsafeRunSync()
} }
test("convert image to pdf") { test("convert image to pdf") {
@ -78,27 +75,25 @@ object ExternConvTest extends SimpleTestSuite with FileChecks {
Seq("{{infile}}", "out", "-l", "deu", "pdf", "txt"), Seq("{{infile}}", "out", "-l", "deu", "pdf", "txt"),
Duration.seconds(20) Duration.seconds(20)
) )
assume(commandExists(cfg.program), s"Command ${cfg.program} not found")
File
.withTempDir[IO](target, "tesseract")
.use(dir =>
IO {
val tessCfg = TesseractConfig(cfg, target)
val (pdf, txt) =
Tesseract
.toPDF[IO, (Path, Path)](tessCfg, Language.German, 8192, blocker, logger)(
ExampleFiles.camera_letter_en_jpg.readURL[IO](8192, blocker),
storePdfTxtHandler(dir.resolve("test.pdf"), dir.resolve("test.txt"))
)
.unsafeRunSync()
if (!commandExists(cfg.program)) ignore(s"Command ${cfg.program} not found") assert(pdf.isNonEmpty && pdf.isPDF)
else assert(txt.isNonEmpty && txt.isPlainText)
File }
.withTempDir[IO](target, "tesseract") )
.use(dir => .unsafeRunSync()
IO {
val tessCfg = TesseractConfig(cfg, target)
val (pdf, txt) =
Tesseract
.toPDF[IO, (Path, Path)](tessCfg, Language.German, 8192, blocker, logger)(
ExampleFiles.camera_letter_en_jpg.readURL[IO](8192, blocker),
storePdfTxtHandler(dir.resolve("test.pdf"), dir.resolve("test.txt"))
)
.unsafeRunSync()
assert(pdf.isNonEmpty && pdf.isPDF)
assert(txt.isNonEmpty && txt.isPlainText)
}
)
.unsafeRunSync()
} }
} }

View File

@ -3,15 +3,14 @@ package docspell.extract.ocr
import cats.effect.IO import cats.effect.IO
import docspell.common.Logger import docspell.common.Logger
import docspell.files.TestFiles import docspell.files.TestFiles
import minitest.SimpleTestSuite import munit._
object TextExtractionSuite extends SimpleTestSuite { class TextExtractionSuite extends FunSuite {
import TestFiles._ import TestFiles._
val logger = Logger.log4s[IO](org.log4s.getLogger) val logger = Logger.log4s[IO](org.log4s.getLogger)
test("extract english pdf") { test("extract english pdf".ignore) {
ignore()
val text = TextExtract val text = TextExtract
.extract[IO](letterSourceEN, blocker, logger, "eng", OcrConfig.default) .extract[IO](letterSourceEN, blocker, logger, "eng", OcrConfig.default)
.compile .compile
@ -20,8 +19,7 @@ object TextExtractionSuite extends SimpleTestSuite {
println(text) println(text)
} }
test("extract german pdf") { test("extract german pdf".ignore) {
ignore()
val expect = TestFiles.letterDEText val expect = TestFiles.letterDEText
val extract = TextExtract val extract = TextExtract
.extract[IO](letterSourceDE, blocker, logger, "deu", OcrConfig.default) .extract[IO](letterSourceDE, blocker, logger, "deu", OcrConfig.default)

View File

@ -2,9 +2,9 @@ package docspell.extract.odf
import cats.effect._ import cats.effect._
import docspell.files.{ExampleFiles, TestFiles} import docspell.files.{ExampleFiles, TestFiles}
import minitest.SimpleTestSuite import munit._
object OdfExtractTest extends SimpleTestSuite { class OdfExtractTest extends FunSuite {
val blocker = TestFiles.blocker val blocker = TestFiles.blocker
implicit val CS = TestFiles.CS implicit val CS = TestFiles.CS

View File

@ -1,8 +1,8 @@
package docspell.extract.pdfbox package docspell.extract.pdfbox
import minitest.SimpleTestSuite import munit._
object PdfMetaDataTest extends SimpleTestSuite { class PdfMetaDataTest extends FunSuite {
test("split keywords on comma") { test("split keywords on comma") {
val md = PdfMetaData.empty.copy(keywords = Some("a,b, c")) val md = PdfMetaData.empty.copy(keywords = Some("a,b, c"))

View File

@ -2,9 +2,9 @@ package docspell.extract.pdfbox
import cats.effect._ import cats.effect._
import docspell.files.{ExampleFiles, TestFiles} import docspell.files.{ExampleFiles, TestFiles}
import minitest.SimpleTestSuite import munit._
object PdfboxExtractTest extends SimpleTestSuite { class PdfboxExtractTest extends FunSuite {
val blocker = TestFiles.blocker val blocker = TestFiles.blocker
implicit val CS = TestFiles.CS implicit val CS = TestFiles.CS

View File

@ -2,11 +2,11 @@ package docspell.extract.pdfbox
import cats.effect._ import cats.effect._
import docspell.files.{ExampleFiles, TestFiles} import docspell.files.{ExampleFiles, TestFiles}
import minitest.SimpleTestSuite import munit._
import java.nio.file.Path import java.nio.file.Path
import fs2.Stream import fs2.Stream
object PdfboxPreviewTest extends SimpleTestSuite { class PdfboxPreviewTest extends FunSuite {
val blocker = TestFiles.blocker val blocker = TestFiles.blocker
implicit val CS = TestFiles.CS implicit val CS = TestFiles.CS

View File

@ -3,9 +3,9 @@ package docspell.extract.poi
import cats.effect._ import cats.effect._
import docspell.common.MimeTypeHint import docspell.common.MimeTypeHint
import docspell.files.{ExampleFiles, TestFiles} import docspell.files.{ExampleFiles, TestFiles}
import minitest.SimpleTestSuite import munit._
object PoiExtractTest extends SimpleTestSuite { class PoiExtractTest extends FunSuite {
val blocker = TestFiles.blocker val blocker = TestFiles.blocker
implicit val CS = TestFiles.CS implicit val CS = TestFiles.CS

View File

@ -1,9 +1,9 @@
package docspell.extract.rtf package docspell.extract.rtf
import docspell.files.ExampleFiles import docspell.files.ExampleFiles
import minitest.SimpleTestSuite import munit._
object RtfExtractTest extends SimpleTestSuite { class RtfExtractTest extends FunSuite {
test("extract text from rtf using java input-stream") { test("extract text from rtf using java input-stream") {
val file = ExampleFiles.examples_sample_rtf val file = ExampleFiles.examples_sample_rtf

View File

@ -2,12 +2,12 @@ package docspell.files
import cats.implicits._ import cats.implicits._
import cats.effect.{Blocker, IO} import cats.effect.{Blocker, IO}
import minitest.SimpleTestSuite import munit._
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
import scala.util.Using import scala.util.Using
object ImageSizeTest extends SimpleTestSuite { class ImageSizeTest extends FunSuite {
val blocker = Blocker.liftExecutionContext(ExecutionContext.global) val blocker = Blocker.liftExecutionContext(ExecutionContext.global)
implicit val CS = IO.contextShift(ExecutionContext.global) implicit val CS = IO.contextShift(ExecutionContext.global)

View File

@ -1,12 +1,12 @@
package docspell.files package docspell.files
import minitest._ import munit._
import cats.effect._ import cats.effect._
import cats.implicits._ import cats.implicits._
import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContext
import docspell.common.Glob import docspell.common.Glob
object ZipTest extends SimpleTestSuite { class ZipTest extends FunSuite {
val blocker = Blocker.liftExecutionContext(ExecutionContext.global) val blocker = Blocker.liftExecutionContext(ExecutionContext.global)
implicit val CS = IO.contextShift(ExecutionContext.global) implicit val CS = IO.contextShift(ExecutionContext.global)

View File

@ -47,7 +47,7 @@ object FindProposal {
ctx.store ctx.store
.transact( .transact(
ROrganization ROrganization
.findLike(coll, mp.values.head.ref.name.toLowerCase) .findLike(coll, mp.values.head.ref.name.toLowerCase, OrgUse.notDisabled)
.map(_.headOption) .map(_.headOption)
) )
.flatTap(oref => .flatTap(oref =>
@ -85,7 +85,11 @@ object FindProposal {
ctx.store ctx.store
.transact( .transact(
REquipment REquipment
.findLike(coll, mp.values.head.ref.name.toLowerCase) .findLike(
coll,
mp.values.head.ref.name.toLowerCase,
EquipmentUse.notDisabled
)
.map(_.headOption) .map(_.headOption)
) )
.flatTap(oref => .flatTap(oref =>
@ -234,7 +238,10 @@ object FindProposal {
case NerTag.Organization => case NerTag.Organization =>
ctx.logger.debug(s"Looking for organizations: $value") *> ctx.logger.debug(s"Looking for organizations: $value") *>
ctx.store ctx.store
.transact(ROrganization.findLike(ctx.args.meta.collective, value)) .transact(
ROrganization
.findLike(ctx.args.meta.collective, value, OrgUse.notDisabled)
)
.map(MetaProposalList.from(MetaProposalType.CorrOrg, nt)) .map(MetaProposalList.from(MetaProposalType.CorrOrg, nt))
case NerTag.Person => case NerTag.Person =>
@ -252,7 +259,10 @@ object FindProposal {
.map(MetaProposalList.from(MetaProposalType.CorrPerson, nt)) .map(MetaProposalList.from(MetaProposalType.CorrPerson, nt))
val s3 = val s3 =
ctx.store ctx.store
.transact(ROrganization.findLike(ctx.args.meta.collective, value)) .transact(
ROrganization
.findLike(ctx.args.meta.collective, value, OrgUse.notDisabled)
)
.map(MetaProposalList.from(MetaProposalType.CorrOrg, nt)) .map(MetaProposalList.from(MetaProposalType.CorrOrg, nt))
ctx.logger.debug(s"Looking for persons and organizations: $value") *> (for { ctx.logger.debug(s"Looking for persons and organizations: $value") *> (for {
ml0 <- s1 ml0 <- s1
@ -268,7 +278,10 @@ object FindProposal {
case NerTag.Misc => case NerTag.Misc =>
ctx.logger.debug(s"Looking for equipments: $value") *> ctx.logger.debug(s"Looking for equipments: $value") *>
ctx.store ctx.store
.transact(REquipment.findLike(ctx.args.meta.collective, value)) .transact(
REquipment
.findLike(ctx.args.meta.collective, value, EquipmentUse.notDisabled)
)
.map(MetaProposalList.from(MetaProposalType.ConcEquip, nt)) .map(MetaProposalList.from(MetaProposalType.ConcEquip, nt))
case NerTag.Email => case NerTag.Email =>

View File

@ -1,10 +1,10 @@
package docspell.joex.analysis package docspell.joex.analysis
import minitest._ import munit._
import NerFile.Pattern import NerFile.Pattern
import java.{util => ju} import java.{util => ju}
object NerFileTest extends SimpleTestSuite { class NerFileTest extends FunSuite {
test("create valid case insensitive patterns") { test("create valid case insensitive patterns") {
val names = List( val names = List(

View File

@ -1,9 +1,9 @@
package docspell.joex.scheduler package docspell.joex.scheduler
import docspell.common.Priority import docspell.common.Priority
import minitest.SimpleTestSuite import munit._
object CountingSchemeSpec extends SimpleTestSuite { class CountingSchemeSpec extends FunSuite {
test("counting") { test("counting") {
val cs = CountingScheme(2, 1) val cs = CountingScheme(2, 1)

View File

@ -5064,6 +5064,7 @@ components:
- id - id
- name - name
- created - created
- use
properties: properties:
id: id:
type: string type: string
@ -5076,6 +5077,12 @@ components:
format: date-time format: date-time
notes: notes:
type: string type: string
use:
type: string
format: equipmentuse
enum:
- concerning
- disabled
ReferenceList: ReferenceList:
description: description:
Listing of entities with their id and a name. Listing of entities with their id and a name.
@ -5119,6 +5126,7 @@ components:
- concerning - concerning
- correspondent - correspondent
- both - both
- disabled
description: | description: |
Whether this person should be used to create suggestions Whether this person should be used to create suggestions
for the "concerning person", "correspondent" or both. for the "concerning person", "correspondent" or both.
@ -5145,6 +5153,7 @@ components:
- address - address
- contacts - contacts
- created - created
- use
properties: properties:
id: id:
type: string type: string
@ -5165,6 +5174,12 @@ components:
format: date-time format: date-time
shortName: shortName:
type: string type: string
use:
type: string
format: orguse
enum:
- correspondent
- disabled
OrganizationList: OrganizationList:
description: | description: |
A list of full organization values. A list of full organization values.

View File

@ -392,7 +392,8 @@ trait Conversions {
v.contacts.map(mkContact).toList, v.contacts.map(mkContact).toList,
ro.notes, ro.notes,
ro.created, ro.created,
ro.shortName ro.shortName,
ro.use
) )
} }
@ -414,7 +415,8 @@ trait Conversions {
v.notes, v.notes,
now, now,
now, now,
v.shortName.map(_.trim) v.shortName.map(_.trim),
v.use
) )
} yield OOrganization.OrgAndContacts(org, cont) } yield OOrganization.OrgAndContacts(org, cont)
} }
@ -439,7 +441,8 @@ trait Conversions {
v.notes, v.notes,
v.created, v.created,
now, now,
v.shortName.map(_.trim) v.shortName.map(_.trim),
v.use
) )
} yield OOrganization.OrgAndContacts(org, cont) } yield OOrganization.OrgAndContacts(org, cont)
} }
@ -628,17 +631,17 @@ trait Conversions {
// equipment // equipment
def mkEquipment(re: REquipment): Equipment = def mkEquipment(re: REquipment): Equipment =
Equipment(re.eid, re.name, re.created, re.notes) Equipment(re.eid, re.name, re.created, re.notes, re.use)
def newEquipment[F[_]: Sync](e: Equipment, cid: Ident): F[REquipment] = def newEquipment[F[_]: Sync](e: Equipment, cid: Ident): F[REquipment] =
timeId.map({ case (id, now) => timeId.map({ case (id, now) =>
REquipment(id, cid, e.name.trim, now, now, e.notes) REquipment(id, cid, e.name.trim, now, now, e.notes, e.use)
}) })
def changeEquipment[F[_]: Sync](e: Equipment, cid: Ident): F[REquipment] = def changeEquipment[F[_]: Sync](e: Equipment, cid: Ident): F[REquipment] =
Timestamp Timestamp
.current[F] .current[F]
.map(now => REquipment(e.id, cid, e.name.trim, e.created, now, e.notes)) .map(now => REquipment(e.id, cid, e.name.trim, e.created, now, e.notes, e.use))
// idref // idref

View File

@ -18,11 +18,12 @@ case class Flags(
fullTextSearchEnabled: Boolean, fullTextSearchEnabled: Boolean,
maxPageSize: Int, maxPageSize: Int,
maxNoteLength: Int, maxNoteLength: Int,
showClassificationSettings: Boolean showClassificationSettings: Boolean,
uiVersion: Int
) )
object Flags { object Flags {
def apply(cfg: Config): Flags = def apply(cfg: Config, uiVersion: Int): Flags =
Flags( Flags(
cfg.appName, cfg.appName,
getBaseUrl(cfg), getBaseUrl(cfg),
@ -32,7 +33,8 @@ object Flags {
cfg.fullTextSearch.enabled, cfg.fullTextSearch.enabled,
cfg.maxItemPageSize, cfg.maxItemPageSize,
cfg.maxNoteLength, cfg.maxNoteLength,
cfg.showClassificationSettings cfg.showClassificationSettings,
uiVersion
) )
private def getBaseUrl(cfg: Config): String = private def getBaseUrl(cfg: Config): String =

View File

@ -133,10 +133,11 @@ object TemplateRoutes {
) )
object IndexData { object IndexData {
private[this] val uiVersion = 2
def apply(cfg: Config): IndexData = def apply(cfg: Config): IndexData =
IndexData( IndexData(
Flags(cfg), Flags(cfg, uiVersion),
chooseUi, chooseUi,
Seq( Seq(
"/app/assets" + Webjars.clipboardjs + "/clipboard.min.js", "/app/assets" + Webjars.clipboardjs + "/clipboard.min.js",
@ -145,7 +146,7 @@ object TemplateRoutes {
), ),
s"/app/assets/docspell-webapp/${BuildInfo.version}/favicon", s"/app/assets/docspell-webapp/${BuildInfo.version}/favicon",
s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell.js", s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell.js",
Flags(cfg).asJson.spaces2 Flags(cfg, uiVersion).asJson.spaces2
) )
private def chooseUi: Seq[String] = private def chooseUi: Seq[String] =

View File

@ -0,0 +1,16 @@
ALTER TABLE "equipment"
ADD COLUMN "equip_use" varchar(254);
UPDATE "equipment" SET "equip_use" = 'concerning';
ALTER TABLE "equipment"
ALTER COLUMN "equip_use" SET NOT NULL;
ALTER TABLE "organization"
ADD COLUMN "org_use" varchar(254);
UPDATE "organization" SET "org_use" = 'correspondent';
ALTER TABLE "organization"
ALTER COLUMN "org_use" SET NOT NULL;

View File

@ -0,0 +1,16 @@
ALTER TABLE `equipment`
ADD COLUMN `equip_use` varchar(254);
UPDATE `equipment` SET `equip_use` = 'concerning';
ALTER TABLE `equipment`
ALTER COLUMN `equip_use` SET NOT NULL;
ALTER TABLE `organization`
ADD COLUMN `org_use` varchar(254);
UPDATE `organization` SET `org_use` = 'correspondent';
ALTER TABLE `organization`
ALTER COLUMN `org_use` SET NOT NULL;

View File

@ -0,0 +1,16 @@
ALTER TABLE "equipment"
ADD COLUMN "equip_use" varchar(254);
UPDATE "equipment" SET "equip_use" = 'concerning';
ALTER TABLE "equipment"
ALTER COLUMN "equip_use" SET NOT NULL;
ALTER TABLE "organization"
ADD COLUMN "org_use" varchar(254);
UPDATE "organization" SET "org_use" = 'correspondent';
ALTER TABLE "organization"
ALTER COLUMN "org_use" SET NOT NULL;

View File

@ -106,6 +106,12 @@ trait DoobieMeta extends EmilDoobieMeta {
implicit val metaPersonUse: Meta[PersonUse] = implicit val metaPersonUse: Meta[PersonUse] =
Meta[String].timap(PersonUse.unsafeFromString)(_.name) Meta[String].timap(PersonUse.unsafeFromString)(_.name)
implicit val metaEquipUse: Meta[EquipmentUse] =
Meta[String].timap(EquipmentUse.unsafeFromString)(_.name)
implicit val metaOrgUse: Meta[OrgUse] =
Meta[String].timap(OrgUse.unsafeFromString)(_.name)
} }
object DoobieMeta extends DoobieMeta { object DoobieMeta extends DoobieMeta {

View File

@ -15,7 +15,8 @@ case class REquipment(
name: String, name: String,
created: Timestamp, created: Timestamp,
updated: Timestamp, updated: Timestamp,
notes: Option[String] notes: Option[String],
use: EquipmentUse
) {} ) {}
object REquipment { object REquipment {
@ -28,7 +29,8 @@ object REquipment {
val created = Column[Timestamp]("created", this) val created = Column[Timestamp]("created", this)
val updated = Column[Timestamp]("updated", this) val updated = Column[Timestamp]("updated", this)
val notes = Column[String]("notes", this) val notes = Column[String]("notes", this)
val all = NonEmptyList.of[Column[_]](eid, cid, name, created, updated, notes) val use = Column[EquipmentUse]("equip_use", this)
val all = NonEmptyList.of[Column[_]](eid, cid, name, created, updated, notes, use)
} }
val T = Table(None) val T = Table(None)
@ -41,7 +43,7 @@ object REquipment {
.insert( .insert(
t, t,
t.all, t.all,
fr"${v.eid},${v.cid},${v.name},${v.created},${v.updated},${v.notes}" fr"${v.eid},${v.cid},${v.name},${v.created},${v.updated},${v.notes},${v.use}"
) )
} }
@ -57,7 +59,8 @@ object REquipment {
t.cid.setTo(v.cid), t.cid.setTo(v.cid),
t.name.setTo(v.name), t.name.setTo(v.name),
t.updated.setTo(now), t.updated.setTo(now),
t.notes.setTo(v.notes) t.notes.setTo(v.notes),
t.use.setTo(v.use)
) )
) )
} yield n } yield n
@ -90,9 +93,17 @@ object REquipment {
sql.query[REquipment].to[Vector] sql.query[REquipment].to[Vector]
} }
def findLike(coll: Ident, equipName: String): ConnectionIO[Vector[IdRef]] = { def findLike(
coll: Ident,
equipName: String,
use: NonEmptyList[EquipmentUse]
): ConnectionIO[Vector[IdRef]] = {
val t = Table(None) val t = Table(None)
run(select(t.eid, t.name), from(t), t.cid === coll && t.name.like(equipName)) run(
select(t.eid, t.name),
from(t),
t.cid === coll && t.name.like(equipName) && t.use.in(use)
)
.query[IdRef] .query[IdRef]
.to[Vector] .to[Vector]
} }

View File

@ -22,7 +22,8 @@ case class ROrganization(
notes: Option[String], notes: Option[String],
created: Timestamp, created: Timestamp,
updated: Timestamp, updated: Timestamp,
shortName: Option[String] shortName: Option[String],
use: OrgUse
) {} ) {}
object ROrganization { object ROrganization {
@ -43,6 +44,7 @@ object ROrganization {
val created = Column[Timestamp]("created", this) val created = Column[Timestamp]("created", this)
val updated = Column[Timestamp]("updated", this) val updated = Column[Timestamp]("updated", this)
val shortName = Column[String]("short_name", this) val shortName = Column[String]("short_name", this)
val use = Column[OrgUse]("org_use", this)
val all = val all =
NonEmptyList.of[Column[_]]( NonEmptyList.of[Column[_]](
oid, oid,
@ -55,7 +57,8 @@ object ROrganization {
notes, notes,
created, created,
updated, updated,
shortName shortName,
use
) )
} }
@ -67,7 +70,7 @@ object ROrganization {
DML.insert( DML.insert(
T, T,
T.all, T.all,
fr"${v.oid},${v.cid},${v.name},${v.street},${v.zip},${v.city},${v.country},${v.notes},${v.created},${v.updated},${v.shortName}" fr"${v.oid},${v.cid},${v.name},${v.street},${v.zip},${v.city},${v.country},${v.notes},${v.created},${v.updated},${v.shortName},${v.use}"
) )
def update(v: ROrganization): ConnectionIO[Int] = { def update(v: ROrganization): ConnectionIO[Int] = {
@ -84,7 +87,8 @@ object ROrganization {
T.country.setTo(v.country), T.country.setTo(v.country),
T.notes.setTo(v.notes), T.notes.setTo(v.notes),
T.updated.setTo(now), T.updated.setTo(now),
T.shortName.setTo(v.shortName) T.shortName.setTo(v.shortName),
T.use.setTo(v.use)
) )
) )
for { for {
@ -109,11 +113,17 @@ object ROrganization {
sql.query[ROrganization].option sql.query[ROrganization].option
} }
def findLike(coll: Ident, orgName: String): ConnectionIO[Vector[IdRef]] = def findLike(
coll: Ident,
orgName: String,
use: NonEmptyList[OrgUse]
): ConnectionIO[Vector[IdRef]] =
run( run(
select(T.oid, T.name), select(T.oid, T.name),
from(T), from(T),
T.cid === coll && (T.name.like(orgName) || T.shortName.like(orgName)) T.cid === coll && (T.name.like(orgName) || T.shortName.like(orgName)) && T.use.in(
use
)
) )
.query[IdRef] .query[IdRef]
.to[Vector] .to[Vector]

View File

@ -3,14 +3,14 @@ package docspell.store.generator
import java.time.LocalDate import java.time.LocalDate
import docspell.store.records._ import docspell.store.records._
import minitest._ import munit._
import docspell.common._ import docspell.common._
import docspell.query.ItemQueryParser import docspell.query.ItemQueryParser
import docspell.store.queries.AttachCountTable import docspell.store.queries.AttachCountTable
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb.generator.{ItemQueryGenerator, Tables} import docspell.store.qb.generator.{ItemQueryGenerator, Tables}
object ItemQueryGeneratorTest extends SimpleTestSuite { class ItemQueryGeneratorTest extends FunSuite {
import docspell.store.impl.DoobieMeta._ import docspell.store.impl.DoobieMeta._
val tables = Tables( val tables = Tables(

View File

@ -1,10 +1,10 @@
package docspell.store.qb package docspell.store.qb
import minitest._ import munit._
import docspell.store.qb.model._ import docspell.store.qb.model._
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
object QueryBuilderTest extends SimpleTestSuite { class QueryBuilderTest extends FunSuite {
test("simple") { test("simple") {
val c = CourseRecord.as("c") val c = CourseRecord.as("c")

View File

@ -1,11 +1,11 @@
package docspell.store.qb.impl package docspell.store.qb.impl
import minitest._ import munit._
import docspell.store.qb._ import docspell.store.qb._
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb.model.{CourseRecord, PersonRecord} import docspell.store.qb.model.{CourseRecord, PersonRecord}
object ConditionBuilderTest extends SimpleTestSuite { class ConditionBuilderTest extends FunSuite {
val c = CourseRecord.as("c") val c = CourseRecord.as("c")
val p = PersonRecord.as("p") val p = PersonRecord.as("p")

View File

@ -1,11 +1,11 @@
package docspell.store.qb.impl package docspell.store.qb.impl
import minitest._ import munit._
import docspell.store.qb._ import docspell.store.qb._
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
import docspell.store.qb.model.{CourseRecord, PersonRecord} import docspell.store.qb.model.{CourseRecord, PersonRecord}
object DSLTest extends SimpleTestSuite { class DSLTest extends FunSuite {
val course = CourseRecord.as("c") val course = CourseRecord.as("c")
val person = PersonRecord.as("p") val person = PersonRecord.as("p")

View File

@ -1,11 +1,11 @@
package docspell.store.qb.impl package docspell.store.qb.impl
import minitest._ import munit._
import docspell.store.qb._ import docspell.store.qb._
import docspell.store.qb.model._ import docspell.store.qb.model._
import docspell.store.qb.DSL._ import docspell.store.qb.DSL._
object SelectBuilderTest extends SimpleTestSuite { class SelectBuilderTest extends FunSuite {
test("basic fragment") { test("basic fragment") {
val c = CourseRecord.as("c") val c = CourseRecord.as("c")

View File

@ -1,8 +1,8 @@
package docspell.store.queries package docspell.store.queries
import minitest._ import munit._
object QueryWildcardTest extends SimpleTestSuite { class QueryWildcardTest extends FunSuite {
test("replace prefix") { test("replace prefix") {
assertEquals("%name", QueryWildcard("*name")) assertEquals("%name", QueryWildcard("*name"))

View File

@ -10,6 +10,8 @@ module Comp.EquipmentForm exposing
import Api.Model.Equipment exposing (Equipment) import Api.Model.Equipment exposing (Equipment)
import Comp.Basic as B import Comp.Basic as B
import Comp.FixedDropdown
import Data.EquipmentUse exposing (EquipmentUse)
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
@ -22,6 +24,8 @@ type alias Model =
{ equipment : Equipment { equipment : Equipment
, name : String , name : String
, notes : Maybe String , notes : Maybe String
, use : EquipmentUse
, useModel : Comp.FixedDropdown.Model EquipmentUse
} }
@ -30,6 +34,11 @@ emptyModel =
{ equipment = Api.Model.Equipment.empty { equipment = Api.Model.Equipment.empty
, name = "" , name = ""
, notes = Nothing , notes = Nothing
, use = Data.EquipmentUse.Concerning
, useModel =
Comp.FixedDropdown.initMap
Data.EquipmentUse.label
Data.EquipmentUse.all
} }
@ -44,6 +53,7 @@ getEquipment model =
, name = model.name , name = model.name
, created = model.equipment.created , created = model.equipment.created
, notes = model.notes , notes = model.notes
, use = Data.EquipmentUse.asString model.use
} }
@ -51,6 +61,7 @@ type Msg
= SetName String = SetName String
| SetEquipment Equipment | SetEquipment Equipment
| SetNotes String | SetNotes String
| UseDropdownMsg (Comp.FixedDropdown.Msg EquipmentUse)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
@ -61,6 +72,9 @@ update _ msg model =
| equipment = t | equipment = t
, name = t.name , name = t.name
, notes = t.notes , notes = t.notes
, use =
Data.EquipmentUse.fromString t.use
|> Maybe.withDefault Data.EquipmentUse.Concerning
} }
, Cmd.none , Cmd.none
) )
@ -71,6 +85,16 @@ update _ msg model =
SetNotes str -> SetNotes str ->
( { model | notes = Util.Maybe.fromString str }, Cmd.none ) ( { model | notes = Util.Maybe.fromString str }, Cmd.none )
UseDropdownMsg lm ->
let
( nm, mu ) =
Comp.FixedDropdown.update lm model.useModel
newUse =
Maybe.withDefault model.use mu
in
( { model | useModel = nm, use = newUse }, Cmd.none )
--- View2 --- View2
@ -102,6 +126,22 @@ view2 model =
] ]
[] []
] ]
, div [ class "mb-4" ]
[ label
[ class S.inputLabel
]
[ text "Use" ]
, Html.map UseDropdownMsg
(Comp.FixedDropdown.view2 (makeUseItem model) model.useModel)
, span [ class "opacity-50 text-sm" ]
[ case model.use of
Data.EquipmentUse.Concerning ->
text "Use as concerning equipment"
Data.EquipmentUse.Disabled ->
text "Do not use for suggestions."
]
]
, div [ class "mb-4" ] , div [ class "mb-4" ]
[ h3 [ class S.header3 ] [ h3 [ class S.header3 ]
[ text "Notes" [ text "Notes"
@ -116,3 +156,9 @@ view2 model =
] ]
] ]
] ]
makeUseItem : Model -> Maybe (Comp.FixedDropdown.Item EquipmentUse)
makeUseItem model =
Just <|
Comp.FixedDropdown.Item model.use (Data.EquipmentUse.label model.use)

View File

@ -8,6 +8,7 @@ module Comp.EquipmentTable exposing
import Api.Model.Equipment exposing (Equipment) import Api.Model.Equipment exposing (Equipment)
import Comp.Basic as B import Comp.Basic as B
import Data.EquipmentUse
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
@ -57,6 +58,9 @@ view2 model =
[ thead [] [ thead []
[ tr [] [ tr []
[ th [ class "" ] [] [ th [ class "" ] []
, th [ class "text-left pr-1 md:px-2 w-20" ]
[ text "Use"
]
, th [ class "text-left" ] [ text "Name" ] , th [ class "text-left" ] [ text "Name" ]
] ]
] ]
@ -72,6 +76,14 @@ renderEquipmentLine2 model equip =
, class S.tableRow , class S.tableRow
] ]
[ B.editLinkTableCell (Select equip) [ B.editLinkTableCell (Select equip)
, td [ class "text-left pr-1 md:px-2" ]
[ div [ class "label inline-flex text-sm" ]
[ Data.EquipmentUse.fromString equip.use
|> Maybe.withDefault Data.EquipmentUse.Concerning
|> Data.EquipmentUse.label
|> text
]
]
, td [ class "text-left" ] , td [ class "text-left" ]
[ text equip.name [ text equip.name
] ]

View File

@ -12,7 +12,9 @@ import Api.Model.Organization exposing (Organization)
import Comp.AddressForm import Comp.AddressForm
import Comp.Basic as B import Comp.Basic as B
import Comp.ContactField import Comp.ContactField
import Comp.FixedDropdown
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.OrgUse exposing (OrgUse)
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
@ -28,6 +30,8 @@ type alias Model =
, contactModel : Comp.ContactField.Model , contactModel : Comp.ContactField.Model
, notes : Maybe String , notes : Maybe String
, shortName : Maybe String , shortName : Maybe String
, use : OrgUse
, useModel : Comp.FixedDropdown.Model OrgUse
} }
@ -39,6 +43,11 @@ emptyModel =
, contactModel = Comp.ContactField.emptyModel , contactModel = Comp.ContactField.emptyModel
, notes = Nothing , notes = Nothing
, shortName = Nothing , shortName = Nothing
, use = Data.OrgUse.Correspondent
, useModel =
Comp.FixedDropdown.initMap
Data.OrgUse.label
Data.OrgUse.all
} }
@ -59,6 +68,7 @@ getOrg model =
, contacts = Comp.ContactField.getContacts model.contactModel , contacts = Comp.ContactField.getContacts model.contactModel
, notes = model.notes , notes = model.notes
, shortName = model.shortName , shortName = model.shortName
, use = Data.OrgUse.asString model.use
} }
@ -69,6 +79,7 @@ type Msg
| ContactMsg Comp.ContactField.Msg | ContactMsg Comp.ContactField.Msg
| SetNotes String | SetNotes String
| SetShortName String | SetShortName String
| UseDropdownMsg (Comp.FixedDropdown.Msg OrgUse)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
@ -87,6 +98,9 @@ update flags msg model =
, name = t.name , name = t.name
, notes = t.notes , notes = t.notes
, shortName = t.shortName , shortName = t.shortName
, use =
Data.OrgUse.fromString t.use
|> Maybe.withDefault Data.OrgUse.Correspondent
} }
, Cmd.batch [ c1, c2 ] , Cmd.batch [ c1, c2 ]
) )
@ -118,11 +132,27 @@ update flags msg model =
, Cmd.none , Cmd.none
) )
UseDropdownMsg lm ->
let
( nm, mu ) =
Comp.FixedDropdown.update lm model.useModel
newUse =
Maybe.withDefault model.use mu
in
( { model | useModel = nm, use = newUse }, Cmd.none )
--- View2 --- View2
makeUseItem : Model -> Maybe (Comp.FixedDropdown.Item OrgUse)
makeUseItem model =
Just <|
Comp.FixedDropdown.Item model.use (Data.OrgUse.label model.use)
view2 : Bool -> UiSettings -> Model -> Html Msg view2 : Bool -> UiSettings -> Model -> Html Msg
view2 mobile settings model = view2 mobile settings model =
div [ class "flex flex-col" ] div [ class "flex flex-col" ]
@ -167,6 +197,22 @@ view2 mobile settings model =
] ]
[] []
] ]
, div [ class "mb-4" ]
[ label
[ class S.inputLabel
]
[ text "Use" ]
, Html.map UseDropdownMsg
(Comp.FixedDropdown.view2 (makeUseItem model) model.useModel)
, span [ class "opacity-50 text-sm" ]
[ case model.use of
Data.OrgUse.Correspondent ->
text "Use as correspondent"
Data.OrgUse.Disabled ->
text "Do not use for suggestions."
]
]
, div [ class "mb-4" ] , div [ class "mb-4" ]
[ h3 [ class S.header3 ] [ h3 [ class S.header3 ]
[ text "Address" [ text "Address"

View File

@ -9,6 +9,7 @@ module Comp.OrgTable exposing
import Api.Model.Organization exposing (Organization) import Api.Model.Organization exposing (Organization)
import Comp.Basic as B import Comp.Basic as B
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.OrgUse
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Styles as S import Styles as S
@ -58,6 +59,9 @@ view2 model =
[ thead [] [ thead []
[ tr [] [ tr []
[ th [ class "" ] [] [ th [ class "" ] []
, th [ class "text-left pr-1 md:px-2" ]
[ text "Use"
]
, th [ class "text-left" ] [ text "Name" ] , th [ class "text-left" ] [ text "Name" ]
, th [ class "text-left hidden md:table-cell" ] [ text "Address" ] , th [ class "text-left hidden md:table-cell" ] [ text "Address" ]
, th [ class "text-left hidden sm:table-cell" ] [ text "Contact" ] , th [ class "text-left hidden sm:table-cell" ] [ text "Contact" ]
@ -75,6 +79,14 @@ renderOrgLine2 model org =
, class S.tableRow , class S.tableRow
] ]
[ B.editLinkTableCell (Select org) [ B.editLinkTableCell (Select org)
, td [ class "text-left pr-1 md:px-2" ]
[ div [ class "label inline-flex text-sm" ]
[ Data.OrgUse.fromString org.use
|> Maybe.withDefault Data.OrgUse.Correspondent
|> Data.OrgUse.label
|> text
]
]
, td [ class "py-4 sm:py-2 pr-2 md:pr-4" ] , td [ class "py-4 sm:py-2 pr-2 md:pr-4" ]
[ text org.name [ text org.name
] ]

View File

@ -229,6 +229,9 @@ view2 mobile settings model =
Data.PersonUse.Both -> Data.PersonUse.Both ->
text "Use as both concerning or correspondent person" text "Use as both concerning or correspondent person"
Data.PersonUse.Disabled ->
text "Do not use for suggestions."
] ]
] ]
, div [ class "mb-4" ] , div [ class "mb-4" ]

View File

@ -33,6 +33,7 @@ import Comp.TagSelect
import Data.CustomFieldChange exposing (CustomFieldValueCollect) import Data.CustomFieldChange exposing (CustomFieldValueCollect)
import Data.Direction exposing (Direction) import Data.Direction exposing (Direction)
import Data.DropdownStyle as DS import Data.DropdownStyle as DS
import Data.EquipmentUse
import Data.Fields import Data.Fields
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.ItemQuery as Q exposing (ItemQuery) import Data.ItemQuery as Q exposing (ItemQuery)
@ -490,7 +491,11 @@ updateDrop ddm flags settings msg model =
SetConcEquip id -> SetConcEquip id ->
let let
equip = equip =
Equipment id.id id.name 0 Nothing Equipment id.id
id.name
0
Nothing
(Data.EquipmentUse.asString Data.EquipmentUse.Concerning)
in in
resetAndSet (ConcEquipmentMsg (Comp.Dropdown.SetSelection [ equip ])) resetAndSet (ConcEquipmentMsg (Comp.Dropdown.SetSelection [ equip ]))

View File

@ -0,0 +1,52 @@
module Data.EquipmentUse exposing
( EquipmentUse(..)
, all
, asString
, fromString
, label
)
import Api.Model.Equipment exposing (Equipment)
type EquipmentUse
= Concerning
| Disabled
fromString : String -> Maybe EquipmentUse
fromString str =
case String.toLower str of
"concerning" ->
Just Concerning
"disabled" ->
Just Disabled
_ ->
Nothing
asString : EquipmentUse -> String
asString pu =
case pu of
Concerning ->
"concerning"
Disabled ->
"disabled"
label : EquipmentUse -> String
label pu =
case pu of
Concerning ->
"Concerning"
Disabled ->
"Disabled"
all : List EquipmentUse
all =
[ Concerning, Disabled ]

View File

@ -0,0 +1,50 @@
module Data.OrgUse exposing
( OrgUse(..)
, all
, asString
, fromString
, label
)
type OrgUse
= Correspondent
| Disabled
fromString : String -> Maybe OrgUse
fromString str =
case String.toLower str of
"correspondent" ->
Just Correspondent
"disabled" ->
Just Disabled
_ ->
Nothing
asString : OrgUse -> String
asString pu =
case pu of
Correspondent ->
"correspondent"
Disabled ->
"disabled"
label : OrgUse -> String
label pu =
case pu of
Correspondent ->
"Correspondent"
Disabled ->
"Disabled"
all : List OrgUse
all =
[ Correspondent, Disabled ]

View File

@ -14,6 +14,7 @@ type PersonUse
= Correspondent = Correspondent
| Concerning | Concerning
| Both | Both
| Disabled
fromString : String -> Maybe PersonUse fromString : String -> Maybe PersonUse
@ -28,6 +29,9 @@ fromString str =
"both" -> "both" ->
Just Both Just Both
"disabled" ->
Just Disabled
_ -> _ ->
Nothing Nothing
@ -44,6 +48,9 @@ asString pu =
Both -> Both ->
"both" "both"
Disabled ->
"disabled"
label : PersonUse -> String label : PersonUse -> String
label pu = label pu =
@ -57,10 +64,13 @@ label pu =
Both -> Both ->
"Both" "Both"
Disabled ->
"Disabled"
all : List PersonUse all : List PersonUse
all = all =
[ Correspondent, Concerning, Both ] [ Correspondent, Concerning, Both, Disabled ]
spanPersonList : List Person -> { concerning : List Person, correspondent : List Person } spanPersonList : List Person -> { concerning : List Person, correspondent : List Person }
@ -86,5 +96,8 @@ spanPersonList input =
| correspondent = p :: res.correspondent | correspondent = p :: res.correspondent
, concerning = p :: res.concerning , concerning = p :: res.concerning
} }
Disabled ->
res
in in
List.foldl merge init input List.foldl merge init input

View File

@ -1,9 +1,11 @@
module Page.Register.Update exposing (update) module Page.Register.Update exposing (update)
import Api import Api
import Api.Model.BasicResult exposing (BasicResult)
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Page exposing (Page(..)) import Page exposing (Page(..))
import Page.Register.Data exposing (..) import Page.Register.Data exposing (..)
import Util.Http
update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
@ -106,8 +108,16 @@ update flags msg model =
, cmd , cmd
) )
SubmitResp (Err _) -> SubmitResp (Err err) ->
( model, Cmd.none ) let
errMsg =
Util.Http.errorToString err
res =
BasicResult False
(errMsg ++ " Please check the form and try again.")
in
( { model | result = Just res }, Cmd.none )
validateForm : Model -> List String validateForm : Model -> List String

View File

@ -164,12 +164,14 @@ errorToString : Http.Error -> String
errorToString error = errorToString error =
let let
f sc = f sc =
case sc of if sc == 404 then
404 -> "The requested resource doesn't exist."
"The requested resource doesn't exist."
_ -> else if sc >= 400 && sc < 500 then
"There was an invalid response status: " ++ String.fromInt sc "Invalid input when processing the request."
else
"There was an invalid response status: " ++ String.fromInt sc ++ "."
in in
errorToStringStatus error f errorToStringStatus error f

View File

@ -34,7 +34,7 @@ if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
fi fi
OPTIONS=c:hsde OPTIONS=c:hsde
LONGOPTS=config:,help,skip,delete,exists LONGOPTS=config:,help,skip,delete,exists,allow-duplicates
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") ! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
@ -46,7 +46,7 @@ fi
# read getopts output this way to handle the quoting right: # read getopts output this way to handle the quoting right:
eval set -- "$PARSED" eval set -- "$PARSED"
exists=n delete=n help=n config="${XDG_CONFIG_HOME:-$HOME/.config}/docspell/ds.conf" exists=n delete=n help=n config="${XDG_CONFIG_HOME:-$HOME/.config}/docspell/ds.conf" dupes=n
while true; do while true; do
case "$1" in case "$1" in
-h|--help) -h|--help)
@ -65,6 +65,10 @@ while true; do
exists=y exists=y
shift shift
;; ;;
--allow-duplicates)
dupes=y
shift
;;
--) --)
shift shift
break break
@ -93,7 +97,16 @@ checkFile() {
upload_file() { upload_file() {
tf=$($MKTEMP_CMD) rc=0 tf=$($MKTEMP_CMD) rc=0
$CURL_CMD -# -o "$tf" --stderr "$tf" -w "%{http_code}" -XPOST -F file=@"$1" "$2" | (2>&1 1>/dev/null grep 200) META1=""
META2=""
if [ "$dupes" = "y" ]; then
META1="-F"
META2="meta={\"multiple\": false, \"skipDuplicates\": false}"
else
META1="-F"
META2="meta={\"multiple\": false, \"skipDuplicates\": true}"
fi
$CURL_CMD -# -o "$tf" --stderr "$tf" -w "%{http_code}" -XPOST $META1 "$META2" -F file=@"$1" "$2" | (2>&1 1>/dev/null grep 200)
rc=$(expr $rc + $?) rc=$(expr $rc + $?)
cat $tf | (2>&1 1>/dev/null grep '{"success":true') cat $tf | (2>&1 1>/dev/null grep '{"success":true')
rc=$(expr $rc + $?) rc=$(expr $rc + $?)
@ -110,12 +123,16 @@ upload_file() {
} }
upload() { upload() {
checkFile "$2" "$1" if [ "$dupes" == "y" ]; then
if [ $? -eq 0 ]; then
info "File already exists at url $2"
return 0
else
upload_file "$1" "$2" upload_file "$1" "$2"
else
checkFile "$2" "$1"
if [ $? -eq 0 ]; then
info "File already exists at url $2"
return 0
else
upload_file "$1" "$2"
fi
fi fi
} }
@ -129,6 +146,7 @@ showUsage() {
info " -d | --delete Delete the files when successfully uploaded (value: $delete)" info " -d | --delete Delete the files when successfully uploaded (value: $delete)"
info " -h | --help Prints this help text. (value: $help)" info " -h | --help Prints this help text. (value: $help)"
info " -e | --exists Checks for the existence of a file instead of uploading (value: $exists)" info " -e | --exists Checks for the existence of a file instead of uploading (value: $exists)"
info " --allow-duplicates Do not skip existing files in docspell (value: $dupes)"
info "" info ""
info "Arguments:" info "Arguments:"
info " One or more files to check for existence or upload." info " One or more files to check for existence or upload."