Skip to content

Commit 2cb033a

Browse files
SkyfaceDAtabayev Muslim
and
Atabayev Muslim
authored
Feat/statistic (#44)
* Добавить зависимость для сессий * Обновить версию ктора с 1.5.0 до 1.5.3 * Добавить статистику Co-authored-by: Atabayev Muslim <[email protected]>
1 parent e21c697 commit 2cb033a

File tree

15 files changed

+192
-6
lines changed

15 files changed

+192
-6
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
/.idea
33
/out
44
/build
5-
/_files
5+
/.files
6+
/.sessions
67
/documentation
78
*.iml
89
*.ipr

Dockerfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ RUN adduser -D -g '' $USER
66

77
# Creating work directory and giving permissions
88
RUN mkdir /app \
9-
&& mkdir /app/_files \
9+
&& mkdir /app/.files \
10+
&& mkdir /app/.sessions \
1011
&& mkdir /app/resources \
1112
&& chown -R $USER /app \
1213
&& chmod -R 755 /app

build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ allprojects {
6969
//Ktor
7070
implementation "io.ktor:ktor-server-netty:$ktorVersion"
7171
implementation "io.ktor:ktor-server-core:$ktorVersion"
72+
implementation "io.ktor:ktor-server-sessions:$ktorVersion"
7273
implementation "io.ktor:ktor-auth:$ktorVersion"
7374
implementation "io.ktor:ktor-auth-jwt:$ktorVersion"
7475
implementation "io.ktor:ktor-gson:$ktorVersion"

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ kotlinVersion=1.4.31
33
hikariVersion=3.4.5
44
exposedVersion=0.29.1
55
postgresqlVersion=42.2.16
6-
ktorVersion=1.5.0
6+
ktorVersion=1.5.3
77
logbackVersion=1.2.1
88
koinVersion=2.2.2
99
shadowVersion=6.1.0

src/kz/seasky/tms/Application.kt

+15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import io.ktor.application.*
44
import io.ktor.features.*
55
import io.ktor.gson.*
66
import io.ktor.server.netty.*
7+
import io.ktor.sessions.*
78
import kz.seasky.tms.di.modules.applicationModule
89
import kz.seasky.tms.di.modules.authenticationModule
910
import kz.seasky.tms.di.modules.databaseModule
@@ -13,8 +14,12 @@ import kz.seasky.tms.features.PermissionFeature
1314
import kz.seasky.tms.features.installAuthentication
1415
import kz.seasky.tms.features.installRouting
1516
import kz.seasky.tms.features.installStatusPages
17+
import kz.seasky.tms.model.statistic.Statistic
1618
import kz.seasky.tms.utils.BuildConfig
19+
import kz.seasky.tms.utils.COOKIE_STATISTIC_NAME
20+
import kz.seasky.tms.utils.SESSION_ROOT_DIR
1721
import org.koin.ktor.ext.Koin
22+
import java.io.File
1823

1924
fun main(args: Array<String>) {
2025
// EngineMain.main(args + arrayOf("-P:args=${args.toList()}"))
@@ -56,6 +61,16 @@ private fun Application.setRestAPI() {
5661
install(CallLogging)
5762
install(CORS)
5863

64+
install(Sessions) {
65+
cookie<Statistic>(
66+
name = COOKIE_STATISTIC_NAME,
67+
storage = directorySessionStorage(File(SESSION_ROOT_DIR), true),
68+
block = {
69+
cookie.maxAgeInSeconds = 5 * 60 * 1000
70+
}
71+
)
72+
}
73+
5974
install(ContentNegotiation) {
6075
gson { serializeNulls() }
6176
}

src/kz/seasky/tms/di/modules/RepositoryModule.kt

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package kz.seasky.tms.di.modules
22

33
import kz.seasky.tms.repository.role.RoleRepository
44
import kz.seasky.tms.repository.role.RoleService
5+
import kz.seasky.tms.repository.statistic.StatisticRepository
6+
import kz.seasky.tms.repository.statistic.StatisticService
7+
import kz.seasky.tms.repository.statistic.StatisticServiceImpl
58
import kz.seasky.tms.repository.status.StatusRepository
69
import kz.seasky.tms.repository.status.StatusService
710
import kz.seasky.tms.repository.task.TaskRepository
@@ -10,6 +13,7 @@ import kz.seasky.tms.repository.user.UserRepository
1013
import kz.seasky.tms.repository.user.UserService
1114
import kz.seasky.tms.utils.FileHelper
1215
import org.koin.dsl.module
16+
import org.koin.experimental.builder.singleBy
1317

1418
val repositoryModule = module {
1519
single { RoleRepository() }
@@ -29,4 +33,7 @@ val repositoryModule = module {
2933
single { UserRepository() }
3034

3135
single { UserService(get(), get()) }
36+
37+
singleBy<StatisticService, StatisticServiceImpl>()
38+
single { StatisticRepository(taskService = get(), fileHelper = get()) }
3239
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package kz.seasky.tms.model.statistic
2+
3+
data class Statistic(
4+
val task: Task,
5+
val disk: Disk
6+
) {
7+
data class Task(val all: Status, val actual: Status) {
8+
data class Status(
9+
val new: Long,
10+
val inWork: Long,
11+
val canceled: Long,
12+
val closed: Long
13+
)
14+
}
15+
16+
data class Disk(val available: Long, val used: Long)
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package kz.seasky.tms.repository.statistic
2+
3+
import kz.seasky.tms.model.statistic.Statistic
4+
import kz.seasky.tms.repository.task.TaskService
5+
import kz.seasky.tms.utils.FileHelper
6+
7+
class StatisticRepository(
8+
private val taskService: TaskService,
9+
private val fileHelper: FileHelper
10+
) {
11+
suspend fun getTasksAll(): Statistic.Task.Status {
12+
return taskService.getAllCount()
13+
}
14+
15+
suspend fun getTasksActual(): Statistic.Task.Status {
16+
return taskService.getActualCount()
17+
}
18+
19+
suspend fun getFileUsedSpace(): Long {
20+
return fileHelper.usedSpace()
21+
}
22+
23+
suspend fun getFileAvailableSpace(): Long {
24+
return fileHelper.availableSpace()
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package kz.seasky.tms.repository.statistic
2+
3+
import io.ktor.sessions.*
4+
import kz.seasky.tms.model.statistic.Statistic
5+
6+
interface StatisticService {
7+
suspend fun getAndUpdateSession(sessions: CurrentSession): Statistic
8+
}
9+
10+
class StatisticServiceImpl(private val repository: StatisticRepository) : StatisticService {
11+
@Suppress("UnnecessaryVariable")
12+
override suspend fun getAndUpdateSession(sessions: CurrentSession): Statistic {
13+
val statistic = Statistic(
14+
task = Statistic.Task(repository.getTasksAll(), repository.getTasksActual()),
15+
disk = Statistic.Disk(repository.getFileUsedSpace(), repository.getFileAvailableSpace())
16+
)
17+
18+
sessions.set(statistic)
19+
20+
return statistic
21+
}
22+
}

src/kz/seasky/tms/repository/task/TaskRepository.kt

+21
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,29 @@ import kz.seasky.tms.model.paging.Paging
1818
import kz.seasky.tms.model.task.*
1919
import org.jetbrains.exposed.exceptions.ExposedSQLException
2020
import org.jetbrains.exposed.sql.*
21+
import org.joda.time.DateTime
2122

2223
class TaskRepository {
24+
fun countAll(): Map<Short, Long> {
25+
return TaskTable
26+
.slice(TaskTable.status, TaskTable.status.count())
27+
.selectAll()
28+
.groupBy(TaskTable.status)
29+
.associate { row ->
30+
row[TaskTable.status].value to row[TaskTable.status.count()]
31+
}
32+
}
33+
34+
fun countAll(dueDate: DateTime): Map<Short, Long> {
35+
return TaskTable
36+
.slice(TaskTable.status, TaskTable.status.count())
37+
.select { TaskTable.dueDate greaterEq dueDate }
38+
.groupBy(TaskTable.status)
39+
.associate { row ->
40+
row[TaskTable.status].value to row[TaskTable.status.count()]
41+
}
42+
}
43+
2344
fun countReceived(userId: UUID, statusId: Short): Long {
2445
return TaskInstanceEntity
2546
.find { (TaskInstanceTable.executor eq userId) and (TaskInstanceTable.status eq statusId) }

src/kz/seasky/tms/repository/task/TaskService.kt

+27
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ import kotlinx.coroutines.withContext
66
import kotlinx.uuid.UUID
77
import kz.seasky.tms.database.TransactionService
88
import kz.seasky.tms.database.tables.task.TaskInstanceEntity
9+
import kz.seasky.tms.enums.Status
910
import kz.seasky.tms.exceptions.ErrorException
1011
import kz.seasky.tms.exceptions.WarningException
1112
import kz.seasky.tms.extensions.asUUID
1213
import kz.seasky.tms.model.file.FileInsert
1314
import kz.seasky.tms.model.paging.Paging
1415
import kz.seasky.tms.model.paging.PagingResponse
16+
import kz.seasky.tms.model.statistic.Statistic
1517
import kz.seasky.tms.model.task.*
1618
import kz.seasky.tms.utils.FILE_DEFAULT_SIZE
1719
import kz.seasky.tms.utils.FileHelper
1820
import kz.seasky.tms.utils.asMiB
21+
import org.joda.time.DateTime
1922
import java.io.File
2023

2124
class TaskService(
@@ -321,4 +324,28 @@ class TaskService(
321324
return@transaction if (file.exists()) file else throw ErrorException("Нет такого файла :c")
322325
}
323326
}
327+
328+
suspend fun getAllCount(): Statistic.Task.Status {
329+
return transactionService.transaction {
330+
val countsByStatus = repository.countAll()
331+
return@transaction countsByStatus.extract()
332+
}
333+
}
334+
335+
suspend fun getActualCount(): Statistic.Task.Status {
336+
return transactionService.transaction {
337+
val currentTime = DateTime.now()
338+
val countsByStatus = repository.countAll(currentTime)
339+
return@transaction countsByStatus.extract()
340+
}
341+
}
342+
343+
private fun Map<Short, Long>.extract(): Statistic.Task.Status {
344+
return Statistic.Task.Status(
345+
new = getOrDefault(Status.New.value, 0),
346+
inWork = getOrDefault(Status.InWork.value, 0),
347+
canceled = getOrDefault(Status.Canceled.value, 0),
348+
closed = getOrDefault(Status.Closed.value, 0)
349+
)
350+
}
324351
}

src/kz/seasky/tms/route/api/v1/Root.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ fun Route.v1() {
1010

1111
authentication()
1212

13+
statistic()
14+
1315
authenticate(JWT_NAME_STANDARD) {
1416
permission()
1517

@@ -22,4 +24,4 @@ fun Route.v1() {
2224
user()
2325
}
2426
}
25-
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package kz.seasky.tms.route.api.v1
2+
3+
import io.ktor.application.*
4+
import io.ktor.response.*
5+
import io.ktor.routing.*
6+
import io.ktor.sessions.*
7+
import kz.seasky.tms.extensions.success
8+
import kz.seasky.tms.model.statistic.Statistic
9+
import kz.seasky.tms.repository.statistic.StatisticService
10+
import org.koin.ktor.ext.inject
11+
12+
fun Route.statistic() {
13+
val service: StatisticService by inject()
14+
15+
get("/smile") { call.respondText("Smile") }
16+
17+
get("/sweet") { call.respondText("Sweet") }
18+
19+
get("/sister") { call.respondText("Sister") }
20+
21+
get("/statistic") {
22+
val statistic = call.sessions.get<Statistic>()?.let { statistic ->
23+
return@let statistic
24+
} ?: service.getAndUpdateSession(call.sessions)
25+
call.success(data = statistic)
26+
}
27+
28+
get("/surprise") { call.respondText("Surprise") }
29+
30+
get("/service") { call.respondText("Service") }
31+
32+
get("/we-are-spirit") { call.respondText("We Are Spirit") }
33+
}

src/kz/seasky/tms/utils/Constants.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ const val MAX_PASSWORD_LENGTH = 32
88
const val JWT_NAME_STANDARD = "jwt-token"
99
const val JWT_CLAIM_ID = "jwt-id"
1010

11-
const val FILE_ROOT_DIR = "./_files"
11+
const val FILE_ROOT_DIR = "./.files"
1212

1313
/** Equals 26214400 bytes or 25MiB */
14-
const val FILE_DEFAULT_SIZE: Long = 25 * 1024 * 1024
14+
const val FILE_DEFAULT_SIZE: Long = 25 * 1024 * 1024
15+
16+
const val SESSION_ROOT_DIR = "./.sessions"
17+
const val COOKIE_STATISTIC_NAME = "cookie-statistic-name"

src/kz/seasky/tms/utils/FileHelper.kt

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import java.io.File
99
import java.io.InputStream
1010

1111
class FileHelper {
12+
private val directory = File(FILE_ROOT_DIR)
13+
1214
companion object {
1315
const val KEY_SUCCESS = "success"
1416
const val KEY_ERROR = "error"
@@ -66,6 +68,14 @@ class FileHelper {
6668
return@withContext output.toByteArray()
6769
}
6870
}
71+
72+
suspend fun usedSpace(): Long = withContext(Dispatchers.IO) {
73+
return@withContext directory.totalSpace - directory.usableSpace
74+
}
75+
76+
suspend fun availableSpace(): Long = withContext(Dispatchers.IO) {
77+
return@withContext directory.usableSpace
78+
}
6979
}
7080

7181
fun Long.asMiB() = this / (1024 * 1024)

0 commit comments

Comments
 (0)