Added project

This commit is contained in:
2023-06-10 23:59:27 +03:00
parent 3ca712c8f6
commit fd63b81d22
8 changed files with 597 additions and 6 deletions

31
.editorconfig Normal file
View File

@@ -0,0 +1,31 @@
# EditorConfig is awesome: https://EditorConfig.org
root = true
[*]
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 160
[*.java]
indent_style = space
indent_size = 4
#https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#ideas-for-domain-specific-properties
wildcard_import_limit = 100
#https://github.com/ec4j/editorconfig-java-domain
java_class_count_to_use_import_on_demand = 100
#https://youtrack.jetbrains.com/issue/IDEA-212525#focus=streamItem-27-3521523.0-0
#https://www.jetbrains.com/help/idea/configuring-code-style.html
ij_java_names_count_to_use_import_on_demand = 100
[*.json]
indent_style = space
indent_size = 2
[*.xml]
indent_style = space
indent_size = 4
[Makefile]
indent_style = tab

121
.gitignore vendored
View File

@@ -1,4 +1,7 @@
# ---> Kotlin # Created by https://www.gitignore.io/api/java,maven,jetbrains
# Edit at https://www.gitignore.io/?templates=java,maven,jetbrains
### Java ###
# Compiled class file # Compiled class file
*.class *.class
@@ -22,5 +25,119 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid* hs_err_pid*
replay_pid*
### JetBrains ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/**/sonarlint/
# SonarQube Plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator/
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
# End of https://www.gitignore.io/api/java,maven,jetbrains
.idea/
*.iml
*.ipr
*.sh
*.log.*
application.properties
application.yaml
/config/
/k8s-ansible/
/Dockerfile
/build/

View File

@@ -2,5 +2,72 @@
1. Утилита считывает поданный на вход xlsx-файл, путь к которому указывается в виде значения параметра при запуске утилиты. 1. Утилита считывает поданный на вход xlsx-файл, путь к которому указывается в виде значения параметра при запуске утилиты.
2. Последовательно считываются строки и столбцы, создаётся объект с заданным набором полей и заносятся в список. 2. Последовательно считываются строки и столбцы, создаётся объект с заданным набором полей и заносятся в список.
3. Список маршаллизуется и сохраняется в файл формата json. Файл имеет то же название, что и xlsx-документ и помещается в тот же 3. Список маршаллизуется и сохраняется в файл формата json. Файл имеет то же название, что и xlsx-документ и помещается в тот же каталог.
каталог.
**Примечания**
***Формат xlsx-документа***
Подаваемый на вход xlsx-документ состоит из одного листа (sheet) и имеет заданный набор столбцов, первая строка это заголовок столбцов, все последующие строки - значения столбцов. Значения в первом столбце заданы в числовом формате типа integer, значения остальных столбцов заданы в строковом формате.
Пример:
| id | field | value | value_map |
| ------ | ------ | ------ | ------ |
| int | string | string | string |
***Формат json***
```
{
"id": 1,
"field": "string",
"value": "string",
"value_map": "string"
}
```
**Расширенная опция**
В дополнении к основной функциональности так же есть возможность прямой записи содержимого xlsx-файл в виде структуры в mongo db.
Структура записи mongo db имеет вид
```
{
"id": 1,
"field": "string",
"value": "string",
"value_map": "string"
}
```
**Инструкция по использованию**
Для запуска консольной программы exceltojson.jar необходимо наличие jdk не ниже версии 8
Программа работает в двух режимах, экспорт в json (по умолчанию) и экспорт в mongo db (нужно задать ключ при запуске программы)
***Общее описание запуска программы (экспорт в json)***
`java -jar exceltojson.jar <полный_путь_к_файлуормата_xlsx>`
на выходе будет файл формата json, имеющий то же название что и входящий файл и будет располагаться в том же каталоге где и входящий файл
Общее описание запуска программы (экспорт в mongo db)
`java -jar exceltojson.jar --mongo=mongodb://<host>:<port>/<database>/<collection_name> <полный_путь_к_файлуормата_xlsx>`
***Примеры запуска***
****Экспорт в json****
`java -jar exceltojson.jar c:\myfolder\myfile.xlsx`
На выходе получим json `c:\myfolder\myfile.json`
****Экспорт в mongo db****
`java -jar exceltojson.jar --mongo=mongodb://mongo-01.testbanki.ru:27017/dict/dictMap c:\myfolder\myfile.xlsx`
Данные из myfile.xlsx экспортируются в mongo db в указанную коллекцию dictMap базы данных dict

191
pom.xml Normal file
View File

@@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>exceltojson</artifactId>
<groupId>ru.resprojects</groupId>
<version>1.0.0.2</version>
<packaging>jar</packaging>
<name>ExcelToJson</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<main.class>ru.resprojects.exceltojson.MainKt</main.class>
</properties>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>1.5.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.5.10</version>
</dependency>
<!-- Excel -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>
<!-- Json -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.12.4</version>
</dependency>
<!-- Logging-->
<dependency>
<groupId>io.github.microutils</groupId>
<artifactId>kotlin-logging-jvm</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.32</version>
</dependency>
<!-- Mongo -->
<dependency>
<groupId>org.litote.kmongo</groupId>
<artifactId>kmongo</artifactId>
<version>4.2.8</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>1.5.10</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.akathist.maven.plugins.launch4j</groupId>
<artifactId>launch4j-maven-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>l4j-clui</id>
<phase>package</phase>
<goals><goal>launch4j</goal></goals>
<configuration>
<headerType>console</headerType>
<outfile>target/${project.artifactId}.exe</outfile>
<jar>target/${project.artifactId}-${project.version}-jar-with-dependencies.jar</jar>
<errTitle>exceltojson</errTitle>
<jre>
<minVersion>1.8</minVersion>
<path>%JAVA_HOME%</path>
<initialHeapSize>128</initialHeapSize>
<maxHeapSize>512</maxHeapSize>
</jre>
<versionInfo>
<fileVersion>${project.version}</fileVersion>
<txtFileVersion>${project.version}</txtFileVersion>
<fileDescription>${project.name}</fileDescription>
<copyright>MIT</copyright>
<productVersion>${project.version}</productVersion>
<txtProductVersion>${project.version}</txtProductVersion>
<productName>${project.name}</productName>
<originalFilename>exceltojson.exe</originalFilename>
<!-- internalName is mandatory -->
<internalName>exceltojson</internalName>
</versionInfo>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,152 @@
package ru.resprojects.exceltojson
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.mongodb.ConnectionString
import com.mongodb.client.MongoCollection
import mu.KotlinLogging
import org.apache.commons.collections4.list.UnmodifiableList
import org.apache.poi.ss.usermodel.CellType
import org.apache.poi.xssf.usermodel.XSSFSheet
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.litote.kmongo.KMongo
import org.litote.kmongo.findOne
import org.litote.kmongo.getCollection
import java.io.File
import java.net.URI
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Paths
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
private val logger = KotlinLogging.logger {}
private val mapper = jacksonObjectMapper()
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("""
usage exceltojson <path_to_excel_file> for generate json file
usage exceltojson --mongo=mongodb://<host>:<port>/<database>/<collection_name> <path_to_file_xlsx> for export to mongo
""".trimIndent())
return
}
if (args.first().startsWith("--mongo=")) {
excelToMongo(args, URI(args.first().substring("--mongo=".length)))
} else {
args.forEach {
if (!it.startsWith("--")) {
println("Start process file $it")
excelToJson(it)
println("------")
}
}
}
}
fun excelToMongo(fileNames: Array<String>, mongoUri: URI) {
try {
val client = KMongo.createClient(ConnectionString(mongoUri.scheme + "://" + mongoUri.host + ":" + mongoUri.port))
client.use { mongoClient ->
val database = mongoClient.getDatabase(mongoUri.path.removePrefix("/").split("/")[0])
val collectionName = mongoUri.path.removePrefix("/").split("/")[1]
val collection = database.getCollection<Dict>(collectionName)
fileNames.forEach { fileName ->
if (!fileName.startsWith("--")) {
println("Start process file $fileName")
val book = XSSFWorkbook(File(fileName).inputStream())
book.use { xssfWorkbook ->
collectToList(xssfWorkbook.getSheetAt(0)).forEach {dict ->
if (!isEntryExistsInMongo(collection, dict)) {
collection.insertOne(dict)
} else {
val message = "--> Entry {id=${dict.id}, field=${dict.field}, value=${dict.value}, value_map=${dict.value_map}} is exist in collection '$collectionName' and was skipped"
println(message)
logger.warn { message }
}
}
}
}
}
}
println("Process is end. All entry was success added to mongo")
println("------")
} catch (e: Exception) {
println("Error process ${e.message}")
logger.error(e) { "Error process" }
}
}
fun isEntryExistsInMongo(collection: MongoCollection<Dict>, entry: Dict): Boolean {
return collection.findOne(
"""
{
"id": ${entry.id},
"field": "${entry.field}",
"value": "${entry.value}",
"value_map": "${entry.value_map}"
}
""".trimIndent()
) != null
}
fun excelToJson(fileName: String) {
try {
if (!Files.exists(Paths.get(fileName))) {
val message = "File $fileName not found"
println(message)
logger.error { message }
return
}
val file = File(fileName)
val book = XSSFWorkbook(file.inputStream())
book.use {
val lst = file.name.split(".")
val defaultFileName = "file-${LocalDateTime.now().format(DateTimeFormatter.ofPattern("ddMMyyyy-HHmm"))}.json"
val outFilename = "${if (file.toPath().parent != null) file.toPath().parent.toString() + FileSystems.getDefault().separator else ""}${if (lst.isNotEmpty()) file.name.split(".")[0] else defaultFileName}.json"
mapper.writeValue(File(outFilename), collectToList(it.getSheetAt(0)))
println("End process file. Write result to $outFilename")
println("-----------")
}
} catch (e: Exception) {
val message = "Error while process file $fileName"
println(message)
logger.error(e) { message }
}
}
fun collectToList(sheet: XSSFSheet): List<Dict> {
val list = ArrayList<Dict>()
if (sheet.first().lastCellNum - sheet.first().firstCellNum < 4) {
throw RuntimeException("Column count < 4")
}
for (i in 1 until sheet.lastRowNum) {
val row = sheet.getRow(i)
if (row.getCell(0) == null) {
continue
}
val id = if (row.getCell(0).cellType == CellType.NUMERIC) {
row.getCell(0).numericCellValue.toInt()
} else {
row.getCell(0).stringCellValue.toInt()
}
val value = if (row.getCell(2).cellType == CellType.NUMERIC) {
row.getCell(2).numericCellValue.toInt().toString()
} else {
row.getCell(2).stringCellValue
}
val valueMap = if (row.getCell(3).cellType == CellType.NUMERIC) {
row.getCell(3).numericCellValue.toInt().toString()
} else {
row.getCell(3).stringCellValue
}
list.add(Dict(
id,
row.getCell(1).stringCellValue,
value,
valueMap,
))
}
return UnmodifiableList(list)
}
data class Dict (var id: Int, var field: String, var value: String, var value_map: String)

View File

@@ -0,0 +1,5 @@
log4j.rootLogger=INFO, file
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%5p [%t] (%F:%L) %-30.90c - %m%n
log4j.appender.file.file=${java.io.tmpdir}/exceltojson.log

View File

@@ -0,0 +1,28 @@
package ru.resprojects.exceltojson
import mu.KotlinLogging
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.junit.jupiter.api.Test
import java.io.File
import java.net.URL
import kotlin.test.assertEquals
data class DictVal (var id: Int, var field: String, var value: String, var value_map: String)
private val logger = KotlinLogging.logger {}
class MainTest {
@Test
fun `when input correct xml file when expected not null list`() {
val book = XSSFWorkbook(File(loadResource("xlsx/test.xlsx").toURI()).inputStream())
book.use {
assertEquals(2, collectToList(book.getSheetAt(0)).size)
}
}
private fun loadResource(path: String): URL {
return Thread.currentThread().contextClassLoader.getResource(path) ?: throw RuntimeException("File $path not found")
}
}

Binary file not shown.