Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/encodings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM eclipse-temurin:21-jre-alpine
LABEL authors="Артемий"
COPY commits.txt /opt/app/commits.txt
COPY ./target/*.jar /opt/app/krok_camp.jar
WORKDIR /opt/app
ENTRYPOINT ["java", "-jar", "krok_camp.jar"]
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ CodeKiller777
Ручной ввод пути к файлу (через консоль, через правку переменной в коде и т.д.) недопустим. Необходимость любых ручных действий с файлами в процессе работы программы будут обнулять решение.

## Автор решения

Павлов Артемий Васильевич
## Описание реализации

Реализован класс RatingManager, который подсчитывает количество коммитов для каждого разработчика (метод calculateCommits),
метод getResult возвращает топ-3 самых активных разработчика, метод validator валидирует данные строки согласно ТЗ.
Вывод в классе Main, чтобы не загружать класс RatingManager. Дополнительно написаны java-docs и обычные комментарии
с дополнительными пояснениями.
## Инструкция по сборке и запуску решения
Собирается с Maven/Gradle/Ant и т.д. Код открывается и запускается в любой IDE, например, Intellij IDEA.
Для удобства есть докер файл, который сам собирает и запускает приложение, для его запуска необходимо получить jar файл (Maven/Gradle/Ant),
он должен находится в папке /target, также необходим файл commits.txt (создайте его в папке проекта). В образе контейнера в папке /opt/app будет находиться файл
result.txt.
36 changes: 36 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>

<groupId>org.example</groupId>
<artifactId>krok_camp</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
<finalName>krok_camp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>
org.example.Main
</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
18 changes: 18 additions & 0 deletions src/main/java/org/example/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.example;
import java.io.File;
import java.io.FileWriter;

public class Main {
public static void main(String[] args) {
try(FileWriter writer = new FileWriter("result.txt")) {
RatingManager manager = new RatingManager(new File("commits.txt").toPath());
manager.calculateCommits();
//Записываем результат в файл
String res = manager.getResult();
if(res != null)
writer.write(res);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
127 changes: 127 additions & 0 deletions src/main/java/org/example/RatingManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package org.example;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

/**Менеджер рейтингов, подсчитывает количество коммитов по именам и выдает трех самых активных участников**/
public class RatingManager {
private Path commitsPath;
private HashMap<String, Integer> commitsCount; //имя, количество коммитов
/**Паттерны для даты и времени**/
private static final String datePattern = "yyyy-MM-dd";
private static final String timePattern = "HH:mm:ss";
/**Форматер для даты**/
private SimpleDateFormat format;

/**Конструктор
* @exception IllegalArgumentException если входной файл отсутствует**/
public RatingManager(Path input) throws IllegalArgumentException {
this.commitsPath = input;
this.commitsCount = new HashMap<>();
this.format = new SimpleDateFormat();
if(!commitsPath.toFile().exists())
throw new IllegalArgumentException("Файл не существует!");
}

/**Валидирует строку согласно ТЗ
* @return null если строка неправильного формата (см ТЗ), иначе возвращает имя пользователя для дальнейшей обработки**/
private String validator(String line) {
if(line == null) return null;
String[] lines = line.split(" ");
//Неправильная строка
if(lines.length != 3)
return null;

//Хеш длины 7 и в нижнем регистре
String hash = lines[1];
if(hash.length() != 7 || !hash.equals(hash.toLowerCase()))
return null;

//Формат даты YYYY-MM-ddTHH:mm:ss
if(lines[2].charAt(10) != 'T') return null;
try {
//Игнорируем T
String date = lines[2].substring(0, 10).intern();
String time = lines[2].substring(11);
//Парсим дату
format.applyPattern(datePattern);
format.parse(date);
format.applyPattern(timePattern);
format.parse(time);
} catch (ParseException e) {
return null;
}

//Имя не начинается с цифры
if(Character.isDigit(lines[0].charAt(0))) {
return null;
}

//Помещаем имя в пул строк, чтобы не было лишнего копирования
return lines[0].intern();
}

/**Подсчет количества коммитов
* @exception IllegalArgumentException если строка неправильного формата
* @exception IOException ошибка чтения файла**/
public void calculateCommits() throws IOException, IllegalArgumentException {
BufferedReader reader = new BufferedReader(new FileReader(commitsPath.toFile()));
String line;
//Обходим строки файла
while ((line = reader.readLine()) != null) {
String name = validator(line);
if(name == null) {
throw new IllegalArgumentException("Неправильный формат строки");
}
//Если уже был, то обновляем счетчик, иначе - добавляем
if(commitsCount.containsKey(name)) {
commitsCount.replace(name, commitsCount.get(name) + 1);
} else {
commitsCount.put(name, 1);
}
}
reader.close();
}

/**Подсчет результатов
* @return имена трех самых активных разработчиков, по одному в каждой строке, null если произошла ошибка или файл пустой**/
public String getResult() {
if(commitsCount == null || commitsCount.isEmpty()) return null;
//Для поиска максимумов
//Количество коммитов, c1 >= c2 >= c3
int c1 = 0, c2 = 0, c3 = 0, temp;
String name1 = "", name2 = "", name3 = "";
for (Map.Entry<String, Integer> entry: commitsCount.entrySet()) {
temp = entry.getValue();
//temp > c1 >= c2 >= c3
if(temp > c1) {
c3 = c2;
c2 = c1;
c1 = temp;
name3 = name2;
name2 = name1;
name1 = entry.getKey();
} else if(temp > c2) { // c1 >= temp > c2 >= c3
c3 = c2;
c2 = temp;
name3 = name2;
name2 = entry.getKey();
} else if(temp > c3) { //c1 >= c2 >= temp > c3
c3 = temp;
name3 = entry.getKey();
}
}
StringJoiner joiner = new StringJoiner("\n");
joiner.add(name1);
joiner.add(name2);
joiner.add(name3);
return joiner.toString();
}
}