From b760e7f8910dd99d8ee9a846d9f746a05c0d3933 Mon Sep 17 00:00:00 2001 From: Ksan Date: Wed, 18 Jun 2025 23:53:24 +0200 Subject: [PATCH] added basic functionality (still need to finish some stuff but will commit so i can continue on my laptop) --- .gitignore | 42 ++++ .idea/.gitignore | 8 + .idea/gradle.xml | 17 ++ .idea/misc.xml | 10 + .idea/vcs.xml | 6 + build.gradle.kts | 25 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 234 ++++++++++++++++++ gradlew.bat | 89 +++++++ settings.gradle.kts | 2 + src/main/java/dev/ksan/DataStore.java | 49 ++++ src/main/java/dev/ksan/DataThread.java | 35 +++ src/main/java/dev/ksan/ETFScraper.java | 98 ++++++++ src/main/java/dev/ksan/MailService.java | 42 ++++ src/main/java/dev/ksan/Main.java | 88 +++++++ .../java/dev/ksan/NotificationMethod.java | 6 + src/main/java/dev/ksan/Subject.java | 234 ++++++++++++++++++ src/main/java/dev/ksan/SubjectEntry.java | 44 ++++ src/main/java/dev/ksan/Subscription.java | 28 +++ src/main/java/dev/ksan/User.java | 118 +++++++++ 21 files changed, 1181 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 build.gradle.kts create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts create mode 100644 src/main/java/dev/ksan/DataStore.java create mode 100644 src/main/java/dev/ksan/DataThread.java create mode 100644 src/main/java/dev/ksan/ETFScraper.java create mode 100644 src/main/java/dev/ksan/MailService.java create mode 100644 src/main/java/dev/ksan/Main.java create mode 100644 src/main/java/dev/ksan/NotificationMethod.java create mode 100644 src/main/java/dev/ksan/Subject.java create mode 100644 src/main/java/dev/ksan/SubjectEntry.java create mode 100644 src/main/java/dev/ksan/Subscription.java create mode 100644 src/main/java/dev/ksan/User.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77eeb37 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ +.env +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..d28243b --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9f48f22 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..e9a0f78 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + id("java") + id("application") +} +group = "dev.ksan" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral(); + maven("https://jitpack.io") + } +application{ + + mainClass.set("dev.ksan.Main") +} +dependencies { + implementation("org.simplejavamail:simple-java-mail:8.7.0") + implementation("net.sourceforge.htmlunit:htmlunit:2.53.0") + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..113fe8a --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "etfOglasi" + diff --git a/src/main/java/dev/ksan/DataStore.java b/src/main/java/dev/ksan/DataStore.java new file mode 100644 index 0000000..8aeeb0a --- /dev/null +++ b/src/main/java/dev/ksan/DataStore.java @@ -0,0 +1,49 @@ +package dev.ksan; + +import java.io.*; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class DataStore { + + public static Map subjectSubscriptions = new ConcurrentHashMap<>(); + private static final String DATA_FILE = "subjects_data.ser"; + static{ + loadSubjectsFromFile(); + } + + public static void notifySubject(Subject subject, SubjectEntry entry){ + subjectSubscriptions.get(subject).notifyUsers(entry); + } + public static synchronized void subscribeUserToSubject(User user, Subject subject) { + if (user != null && subject != null && subjectSubscriptions.containsKey(subject)) { + subjectSubscriptions.get(subject).subscribe(user); + user.addSubject(subject); + } + } + + public synchronized void unsubscribeUserFromSubject(User user, Subject subject) { + if (user != null && subject != null && subjectSubscriptions.containsKey(subject)) { + Subscription subscription = subjectSubscriptions.get(subject); + if (subscription != null) { + subscription.unsubscribe(user); + } + } + } + private static void loadSubjectsFromFile() { + try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(DATA_FILE))) { + subjectSubscriptions = (Map) in.readObject(); + } catch (IOException | ClassNotFoundException e) { + System.out.println("Error loading subjects data: " + e.getMessage()); + } + } + + private static void saveSubjectsToFile() { + try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(DATA_FILE))) { + out.writeObject(subjectSubscriptions); + } catch (IOException e) { + System.out.println("Error saving subjects data: " + e.getMessage()); + } + } + +} diff --git a/src/main/java/dev/ksan/DataThread.java b/src/main/java/dev/ksan/DataThread.java new file mode 100644 index 0000000..1aaf8e2 --- /dev/null +++ b/src/main/java/dev/ksan/DataThread.java @@ -0,0 +1,35 @@ +package dev.ksan; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class DataThread implements Runnable { + private List allEntries = new ArrayList<>(); + private boolean running; + Set subjects = Subject.generateSubjects(); + @Override + public void run() { + while(running){ + + } + } + public void compare(List subjectEntries) { + for(SubjectEntry subjectEntry : subjectEntries){ + if(!allEntries.contains(subjectEntry)){ + + for(Subject subject : subjects){ + if(subject.matchesTitle(subjectEntry.getTitle())){ + DataStore.notifySubject(subject, subjectEntry); + allEntries.add(subjectEntry); + } + } + + } + } + } + + public void stop(){ + running = false; + } +} diff --git a/src/main/java/dev/ksan/ETFScraper.java b/src/main/java/dev/ksan/ETFScraper.java new file mode 100644 index 0000000..3ec39ab --- /dev/null +++ b/src/main/java/dev/ksan/ETFScraper.java @@ -0,0 +1,98 @@ +package dev.ksan; + +import com.gargoylesoftware.htmlunit.BrowserVersion; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.DomElement; +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import java.util.ArrayList; +import java.util.List; + +public class ETFScraper implements Runnable { + private List entries = new ArrayList<>(); + + private volatile boolean running = true; + + public ETFScraper() { + } + private static String getTextOrEmpty(HtmlElement parent, String xPath) { + HtmlElement element = parent.getFirstByXPath(xPath); + return element == null ? "" : element.asNormalizedText(); + } + List getAllEntries() { + return entries; + } + + @Override + public void run() { + while(running) { + try (final WebClient webClient = new WebClient(BrowserVersion.FIREFOX)) { + + webClient.getOptions().setJavaScriptEnabled(true); + webClient.getOptions().setCssEnabled(false); + webClient.getOptions().setThrowExceptionOnScriptError(false); + + HtmlPage mainPage = webClient.getPage("https://efee.etf.unibl.org/oglasi/"); + webClient.waitForBackgroundJavaScript(1000); + + List rawToggles = mainPage.getByXPath("//a[@href='#']"); + List toggles = new ArrayList<>(); + for (DomElement el : rawToggles) { + if (el instanceof HtmlAnchor) { + toggles.add((HtmlAnchor) el); + } + } + int ul_idSelection = 1; + for (HtmlAnchor anchor : toggles) { + String groupName = anchor.asNormalizedText().split("\n")[0].trim(); + System.out.println("Group name: " + groupName); + HtmlPage updatedPage = anchor.click(); + webClient.waitForBackgroundJavaScript(1000); + + String ul_id = "ul_id_" + Integer.toString(ul_idSelection); + + DomElement rawElement = updatedPage.getElementById(ul_id); + HtmlElement listElement = rawElement instanceof HtmlElement ? (HtmlElement) rawElement : null; + + if (listElement == null) { + System.out.println("An element with id " + ul_id + " was not found"); + ul_idSelection++; + continue; + } + + List items = listElement.getElementsByTagName("li"); + for (HtmlElement item : items) { + String title = getTextOrEmpty(item, ".//h1"); + String date = getTextOrEmpty(item, ".//h2[1]"); + String info = getTextOrEmpty(item, ".//h2[2]"); + List paragraphs = new ArrayList<>(); + List pTags = item.getByXPath(".//p"); + for (HtmlElement pTag : pTags) { + paragraphs.add(pTag.asNormalizedText()); + } + SubjectEntry entry = new SubjectEntry(title, date, info, paragraphs); + + entries.add(entry); + + + } + + ul_idSelection++; + } + + Thread.sleep(20000); + + } catch (Exception e) { + e.printStackTrace(); + System.out.println("ERROR: " + e.getMessage()); + } + } + System.out.println("WebScraper thread stopped"); + } + public void stop(){ + running = false; + } + +} \ No newline at end of file diff --git a/src/main/java/dev/ksan/MailService.java b/src/main/java/dev/ksan/MailService.java new file mode 100644 index 0000000..1cd68f5 --- /dev/null +++ b/src/main/java/dev/ksan/MailService.java @@ -0,0 +1,42 @@ +package dev.ksan; + +import org.simplejavamail.api.email.Email; +import org.simplejavamail.api.mailer.Mailer; +import org.simplejavamail.api.mailer.config.TransportStrategy; +import org.simplejavamail.email.EmailBuilder; +import org.simplejavamail.mailer.MailerBuilder; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +public class MailService { + + String address; + String passwd; + Mailer mailer; + + public MailService(String filename) { + try(BufferedReader br = new BufferedReader((new FileReader(filename)))) { + address = br.readLine(); + System.out.println(address); + passwd = br.readLine(); + }catch(IOException e) { + e.printStackTrace(); + } + this.mailer = MailerBuilder + .withSMTPServer("smtp.gmail.com",587,address,passwd) + .withTransportStrategy(TransportStrategy.SMTP) + .withSessionTimeout(10_000).buildMailer(); + } + + public void sendEmail(String to, String subject, String body) { + Email email = EmailBuilder.startingBlank() + .from(address).to(to) + .withSubject(subject) + .withPlainText(body).buildEmail(); + + mailer.sendMail(email); + } +} diff --git a/src/main/java/dev/ksan/Main.java b/src/main/java/dev/ksan/Main.java new file mode 100644 index 0000000..38e0332 --- /dev/null +++ b/src/main/java/dev/ksan/Main.java @@ -0,0 +1,88 @@ +package dev.ksan; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.BrowserVersion; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.*; + +import java.io.IOException; +import java.util.*; + +public class Main { + + public static void main(String[] args) { + boolean running = true; + User user = new User("djordje@ksan.dev","123"); + boolean istrue = user.addSubject("Filozofija"); + user.addSubject("Programiranje 2"); + System.out.println(istrue); + System.out.println(user.getEmail() + user.getId()); + + Set subjectSet = user.getSubjectSet(); + + List entries = new ArrayList<>(); + List newEntries = new ArrayList<>(); + + + + ETFScraper task = new ETFScraper(); + + + Thread webClientThread = new Thread(task, "WebClientThread"); + webClientThread.start(); + + Scanner scanner = new Scanner(System.in); + + try { + while (running) { + + String command = scanner.nextLine(); + + switch (command) { + case "stop": + task.stop(); + running = false; + System.out.println("Stopping..."); + break; + case "list": + System.out.println(); + + } + + + } + }catch (Exception e) { + scanner.close(); + e.printStackTrace(); + } + scanner.close(); + //temp + SubjectEntry mail = new SubjectEntry("TAAAA","asd", "Test", Arrays.asList("Testing mail broj 2", "ne znam zas je u spamu")); + try { + //user.sendEmail(mail); + System.out.println("AAAAAAAAAAAAAAAAAAAA"); + } catch (Exception e) { + throw new RuntimeException(e); + } + + entries=task.getAllEntries(); + System.out.println("BBBBBBBBBBBBb"); + System.out.println(entries.size()); + + for(SubjectEntry e : task.getAllEntries()) { + //System.out.println(e); + } + for(Subject subject : user.getSubjectSet()) { + + for(SubjectEntry entry : entries){ + if(subject.isSubject(entry.getTitle())){ + //user.sendEmail(entry); + System.out.println("Subject " + subject.getTitle() + " was found"); + } + } + + } + //temp + + } +} \ No newline at end of file diff --git a/src/main/java/dev/ksan/NotificationMethod.java b/src/main/java/dev/ksan/NotificationMethod.java new file mode 100644 index 0000000..e61df19 --- /dev/null +++ b/src/main/java/dev/ksan/NotificationMethod.java @@ -0,0 +1,6 @@ +package dev.ksan; + +public enum NotificationMethod{ + EMAIL, + PUSH_NOTIFICATION; +} diff --git a/src/main/java/dev/ksan/Subject.java b/src/main/java/dev/ksan/Subject.java new file mode 100644 index 0000000..7f7bdd7 --- /dev/null +++ b/src/main/java/dev/ksan/Subject.java @@ -0,0 +1,234 @@ +package dev.ksan; + +import java.io.Serializable; +import java.text.Normalizer; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class Subject implements Serializable { + public String title; + public Set alternatives = new HashSet<>(); + + public Subject(String title){ + this.title = title; + alternatives.add(title); + } + public Subject(String title, Set alternatives) { + this.title = title; + this.alternatives = alternatives; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Subject)) return false; + Subject subject = (Subject) o; + if(Objects.equals(title, subject.title)){ + return true; + } + if(alternatives.equals(subject.alternatives)){ + return true; + } + for(String alt:subject.alternatives){ + if(this.alternatives.contains(alt)){ + return true; + } + } + return false; + } + private String normalizeText(String text) { + return Normalizer.normalize(text.toLowerCase(), Normalizer.Form.NFD).replaceAll("\\p{M}", ""); + } + public boolean matchesTitle(String title) { + String normalizedTitle = normalizeText(title); + // Check direct match with subject title + if (normalizedTitle.contains(normalizeText(this.title))) { + return true; + } + // Check alternatives + for (String alt : alternatives) { + if (normalizedTitle.contains(normalizeText(alt))) { + return true; + } + } + return false; + } + @Override + public int hashCode() { + return Objects.hash(title); + } + + @Override + public String toString() { + return title; + } + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + alternatives.add(title); + } + + public boolean isSubject(String name) { + for(String alt : alternatives){ + if(name.contains(alt)){ + return true; + } + } + return false; + } + + // TODO works for now but change to check website and get the list of subjects from there and add function to + // generate alternative versions of that subject name + // "https://etf.unibl.org/studiranje/1-ciklus/pl-n-i-r-sp-r-d-n-s-v.html" does not have the full list of subjects?? dodati prebmete sa smjerova rucno do tad mozda?? + public static Set generateSubjects(){ + Set subjects = new HashSet<>(); + + subjects.add(new Subject("Savremene telekomunikacije", Set.of("Савремене телекомуникације", "савремене телекомуникације", "Savremene telekomunikacije", "САВРЕМЕНЕ ТЕЛЕКОМУНИКАЦИЈЕ", "SAVREMENE TELEKOMUNIKACIJE", "savremene telekomunikacije"))); + subjects.add(new Subject("Mrežno i distribuirano programiranje", Set.of("Мрежно и дистрибуирано програмирање", "MREZNO I DISTRIBUIRANO PROGRAMIRANJE", "mrezno i distribuirano programiranje", "mrežno i distribuirano programiranje", "мрежно и дистрибуирано програмирање", "Mrezno i distribuirano programiranje", "МРЕЖНО И ДИСТРИБУИРАНО ПРОГРАМИРАЊЕ", "MREŽNO I DISTRIBUIRANO PROGRAMIRANJE", "Mrežno i distribuirano programiranje"))); + subjects.add(new Subject("Osnovi sistema automatskog upravljanja", Set.of("основи система аутоматског управљања", "Основи система аутоматског управљања", "ОСНОВИ СИСТЕМА АУТОМАТСКОГ УПРАВЉАЊА", "Osnovi sistema automatskog upravljanja", "osnovi sistema automatskog upravljanja", "OSNOVI SISTEMA AUTOMATSKOG UPRAVLJANJA"))); + subjects.add(new Subject("Tehnika visokog napona", Set.of("TEHNIKA VISOKOG NAPONA", "Техника високог напона", "ТЕХНИКА ВИСОКОГ НАПОНА", "tehnika visokog napona", "техника високог напона", "Tehnika visokog napona"))); + subjects.add(new Subject("Sinteza sistema automatskog upravljanja", Set.of("Синтеза система аутоматског управљања", "sinteza sistema automatskog upravljanja", "SINTEZA SISTEMA AUTOMATSKOG UPRAVLJANJA", "синтеза система аутоматског управљања", "Sinteza sistema automatskog upravljanja", "СИНТЕЗА СИСТЕМА АУТОМАТСКОГ УПРАВЉАЊА"))); + subjects.add(new Subject("Telekomunikacione mreže", Set.of("Telekomunikacione mreže", "telekomunikacione mreže", "ТЕЛЕКОМУНИКАЦИОНЕ МРЕЖЕ", "telekomunikacione mreze", "телекомуникационе мреже", "TELEKOMUNIKACIONE MREŽE", "Телекомуникационе мреже", "Telekomunikacione mreze", "TELEKOMUNIKACIONE MREZE"))); + subjects.add(new Subject("Multimedijalni signali i sistemi", Set.of("мултимедијални сигнали и системи", "multimedijalni signali i sistemi", "МУЛТИМЕДИЈАЛНИ СИГНАЛИ И СИСТЕМИ", "Мултимедијални сигнали и системи", "MULTIMEDIJALNI SIGNALI I SISTEMI", "Multimedijalni signali i sistemi"))); + subjects.add(new Subject("Osnovi softverskog inženjerstva", Set.of("Osnovi softverskog inženjerstva", "ОСНОВИ СОФТВЕРСКОГ ИНЖЕЊЕРСТВА", "Osnovi softverskog inzenjerstva", "osnovi softverskog inženjerstva", "osnovi softverskog inzenjerstva", "OSNOVI SOFTVERSKOG INZENJERSTVA", "OSNOVI SOFTVERSKOG INŽENJERSTVA", "Основи софтверског инжењерства", "основи софтверског инжењерства"))); + subjects.add(new Subject("Kriptografija i računarska zaštita", Set.of("KRIPTOGRAFIJA I RAČUNARSKA ZAŠTITA", "Kriptografija i racunarska zastita", "kriptografija i računarska zaštita", "KRIPTOGRAFIJA I RACUNARSKA ZASTITA", "Kriptografija i računarska zaštita", "КРИПТОГРАФИЈА И РАЧУНАРСКА ЗАШТИТА", "kriptografija i racunarska zastita", "Криптографија и рачунарска заштита", "криптографија и рачунарска заштита"))); + subjects.add(new Subject("Operativni sistemi i programiranje u realnom vremenu", Set.of("Оперативни системи и програмирање у реалном времену", "operativni sistemi i programiranje u realnom vremenu", "ОПЕРАТИВНИ СИСТЕМИ И ПРОГРАМИРАЊЕ У РЕАЛНОМ ВРЕМЕНУ", "Operativni sistemi i programiranje u realnom vremenu", "OPERATIVNI SISTEMI I PROGRAMIRANJE U REALNOM VREMENU", "оперативни системи и програмирање у реалном времену"))); + subjects.add(new Subject("Vještine komuniciranja", Set.of("Vještine komuniciranja", "Вјештине комуницирања", "vještine komuniciranja", "vjestine komuniciranja", "VJEŠTINE KOMUNICIRANJA", "ВЈЕШТИНЕ КОМУНИЦИРАЊА", "вјештине комуницирања", "Vjestine komuniciranja", "VJESTINE KOMUNICIRANJA"))); + subjects.add(new Subject("Uvod u teoriju sistema", Set.of("uvod u teoriju sistema", "UVOD U TEORIJU SISTEMA", "увод у теорију система", "УВОД У ТЕОРИЈУ СИСТЕМА", "Uvod u teoriju sistema", "Увод у теорију система"))); + subjects.add(new Subject("Električne mašine 2", Set.of("Elektricne masine 2", "Електричне машине 2", "električne mašine 2", "ELEKTRICNE MASINE 2", "ЕЛЕКТРИЧНЕ МАШИНЕ 2", "ELEKTRIČNE MAŠINE 2", "електричне машине 2", "elektricne masine 2", "Električne mašine 2"))); + subjects.add(new Subject("Mikroprocesori", Set.of("mikroprocesori", "Микропроцесори", "микропроцесори", "Mikroprocesori", "MIKROPROCESORI", "МИКРОПРОЦЕСОРИ"))); + subjects.add(new Subject("Elektromotorni pogoni", Set.of("Електромоторни погони", "ELEKTROMOTORNI POGONI", "електромоторни погони", "Elektromotorni pogoni", "ЕЛЕКТРОМОТОРНИ ПОГОНИ", "elektromotorni pogoni"))); + subjects.add(new Subject("Digitalna obrada signala", Set.of("Digitalna obrada signala", "digitalna obrada signala", "Дигитална обрада сигнала", "DIGITALNA OBRADA SIGNALA", "дигитална обрада сигнала", "ДИГИТАЛНА ОБРАДА СИГНАЛА"))); + subjects.add(new Subject("Digitalna televizija", Set.of("Digitalna televizija", "Дигитална телевизија", "digitalna televizija", "дигитална телевизија", "DIGITALNA TELEVIZIJA", "ДИГИТАЛНА ТЕЛЕВИЗИЈА"))); + subjects.add(new Subject("Projekat iz elektroenergetike", Set.of("ПРОЈЕКАТ ИЗ ЕЛЕКТРОЕНЕРГЕТИКЕ", "projekat iz elektroenergetike", "Projekat iz elektroenergetike", "Пројекат из електроенергетике", "пројекат из електроенергетике", "PROJEKAT IZ ELEKTROENERGETIKE"))); + subjects.add(new Subject("Kućna automatizacija", Set.of("КУЋНА АУТОМАТИЗАЦИЈА", "KUCNA AUTOMATIZACIJA", "Kućna automatizacija", "кућна аутоматизација", "kućna automatizacija", "Кућна аутоматизација", "Kucna automatizacija", "kucna automatizacija", "KUĆNA AUTOMATIZACIJA"))); + subjects.add(new Subject("Razvodna postrojenja i aparati", Set.of("razvodna postrojenja i aparati", "RAZVODNA POSTROJENJA I APARATI", "разводна постројења и апарати", "Razvodna postrojenja i aparati", "РАЗВОДНА ПОСТРОЈЕЊА И АПАРАТИ", "Разводна постројења и апарати"))); + subjects.add(new Subject("Performanse računarskih sistema", Set.of("Performanse racunarskih sistema", "performanse racunarskih sistema", "Перформансе рачунарских система", "performanse računarskih sistema", "Performanse računarskih sistema", "перформансе рачунарских система", "PERFORMANSE RACUNARSKIH SISTEMA", "ПЕРФОРМАНСЕ РАЧУНАРСКИХ СИСТЕМА", "PERFORMANSE RAČUNARSKIH SISTEMA"))); + subjects.add(new Subject("Mobilni radio sistemi", Set.of("мобилни радио системи", "MOBILNI RADIO SISTEMI", "Мобилни радио системи", "mobilni radio sistemi", "МОБИЛНИ РАДИО СИСТЕМИ", "Mobilni radio sistemi"))); + subjects.add(new Subject("Programiranje u realnom vremenu", Set.of("PROGRAMIRANJE U REALNOM VREMENU", "програмирање у реалном времену", "Програмирање у реалном времену", "programiranje u realnom vremenu", "ПРОГРАМИРАЊЕ У РЕАЛНОМ ВРЕМЕНУ", "Programiranje u realnom vremenu"))); + subjects.add(new Subject("Računarske mreže", Set.of("RAČUNARSKE MREŽE", "рачунарске мреже", "Racunarske mreze", "RACUNARSKE MREZE", "Рачунарске мреже", "racunarske mreze", "računarske mreže", "Računarske mreže", "РАЧУНАРСКЕ МРЕЖЕ"))); + subjects.add(new Subject("Telekomunikacioni sistemi", Set.of("Телекомуникациони системи", "TELEKOMUNIKACIONI SISTEMI", "телекомуникациони системи", "Telekomunikacioni sistemi", "ТЕЛЕКОМУНИКАЦИОНИ СИСТЕМИ", "telekomunikacioni sistemi"))); + subjects.add(new Subject("Strukture podataka i algoritmi", Set.of("СТРУКТУРЕ ПОДАТАКА И АЛГОРИТМИ", "структуре података и алгоритми", "Strukture podataka i algoritmi", "strukture podataka i algoritmi", "STRUKTURE PODATAKA I ALGORITMI", "Структуре података и алгоритми"))); + subjects.add(new Subject("Operaciona istraživanja", Set.of("operaciona istrazivanja", "operaciona istraživanja", "операциона истраживања", "Operaciona istrazivanja", "ОПЕРАЦИОНА ИСТРАЖИВАЊА", "OPERACIONA ISTRAŽIVANJA", "Operaciona istraživanja", "Операциона истраживања", "OPERACIONA ISTRAZIVANJA"))); + subjects.add(new Subject("Stručna praksa", Set.of("Стручна пракса", "Strucna praksa", "Stručna praksa", "СТРУЧНА ПРАКСА", "STRUČNA PRAKSA", "stručna praksa", "стручна пракса", "strucna praksa", "STRUCNA PRAKSA"))); + subjects.add(new Subject("Odabrana poglavlja operativnih sistema", Set.of("Одабрана поглавља оперативних система", "ODABRANA POGLAVLJA OPERATIVNIH SISTEMA", "одабрана поглавља оперативних система", "Odabrana poglavlja operativnih sistema", "ОДАБРАНА ПОГЛАВЉА ОПЕРАТИВНИХ СИСТЕМА", "odabrana poglavlja operativnih sistema"))); + subjects.add(new Subject("Senzori i aktuatori", Set.of("Сензори и актуатори", "senzori i aktuatori", "СЕНЗОРИ И АКТУАТОРИ", "SENZORI I AKTUATORI", "Senzori i aktuatori", "сензори и актуатори"))); + subjects.add(new Subject("Projektovanje elektronskih uređaja", Set.of("PROJEKTOVANJE ELEKTRONSKIH UREĐAJA", "Пројектовање електронских уређаја", "ПРОЈЕКТОВАЊЕ ЕЛЕКТРОНСКИХ УРЕЂАЈА", "projektovanje elektronskih uređaja", "пројектовање електронских уређаја", "Projektovanje elektronskih uređaja", "PROJEKTOVANJE ELEKTRONSKIH UREDJAJA", "Projektovanje elektronskih uredjaja", "projektovanje elektronskih uredjaja"))); + subjects.add(new Subject("Projektovanje namjenskih računarskih sistema", Set.of("PROJEKTOVANJE NAMJENSKIH RAČUNARSKIH SISTEMA", "PROJEKTOVANJE NAMJENSKIH RACUNARSKIH SISTEMA", "Пројектовање намјенских рачунарских система", "ПРОЈЕКТОВАЊЕ НАМЈЕНСКИХ РАЧУНАРСКИХ СИСТЕМА", "Projektovanje namjenskih računarskih sistema", "projektovanje namjenskih racunarskih sistema", "projektovanje namjenskih računarskih sistema", "пројектовање намјенских рачунарских система", "Projektovanje namjenskih racunarskih sistema"))); + subjects.add(new Subject("Digitalna obrada slike", Set.of("дигитална обрада слике", "DIGITALNA OBRADA SLIKE", "Дигитална обрада слике", "ДИГИТАЛНА ОБРАДА СЛИКЕ", "digitalna obrada slike", "Digitalna obrada slike"))); + subjects.add(new Subject("Arhitektura interneta", Set.of("Arhitektura interneta", "Архитектура интернета", "архитектура интернета", "arhitektura interneta", "АРХИТЕКТУРА ИНТЕРНЕТА", "ARHITEKTURA INTERNETA"))); + subjects.add(new Subject("Akustika i audio tehnika", Set.of("Akustika i audio tehnika", "AKUSTIKA I AUDIO TEHNIKA", "АКУСТИКА И АУДИО ТЕХНИКА", "Акустика и аудио техника", "акустика и аудио техника", "akustika i audio tehnika"))); + subjects.add(new Subject("Matematika 3", Set.of("MATEMATIKA 3", "matematika 3", "математика 3", "Математика 3", "МАТЕМАТИКА 3", "Matematika 3"))); + subjects.add(new Subject("Signali i sistemi", Set.of("сигнали и системи", "СИГНАЛИ И СИСТЕМИ", "signali i sistemi", "SIGNALI I SISTEMI", "Сигнали и системи", "Signali i sistemi"))); + subjects.add(new Subject("Robotika", Set.of("ROBOTIKA", "РОБОТИКА", "Robotika", "роботика", "Роботика", "robotika"))); + subjects.add(new Subject("Osnovi elektronike i digitalne tehnike", Set.of("основи електронике и дигиталне технике", "osnovi elektronike i digitalne tehnike", "ОСНОВИ ЕЛЕКТРОНИКЕ И ДИГИТАЛНЕ ТЕХНИКЕ", "OSNOVI ELEKTRONIKE I DIGITALNE TEHNIKE", "Osnovi elektronike i digitalne tehnike", "Основи електронике и дигиталне технике"))); + subjects.add(new Subject("Upravljanje u realnom vremenu", Set.of("УПРАВЉАЊЕ У РЕАЛНОМ ВРЕМЕНУ", "upravljanje u realnom vremenu", "Upravljanje u realnom vremenu", "UPRAVLJANJE U REALNOM VREMENU", "управљање у реалном времену", "Управљање у реалном времену"))); + subjects.add(new Subject("Funkcionalna verifikacija hardvera", Set.of("функционална верификација хардвера", "Funkcionalna verifikacija hardvera", "Функционална верификација хардвера", "ФУНКЦИОНАЛНА ВЕРИФИКАЦИЈА ХАРДВЕРА", "funkcionalna verifikacija hardvera", "FUNKCIONALNA VERIFIKACIJA HARDVERA"))); + subjects.add(new Subject("Nelinearni sistemi", Set.of("НЕЛИНЕАРНИ СИСТЕМИ", "Nelinearni sistemi", "Нелинеарни системи", "NELINEARNI SISTEMI", "nelinearni sistemi", "нелинеарни системи"))); + subjects.add(new Subject("Matematika 2", Set.of("МАТЕМАТИКА 2", "matematika 2", "математика 2", "MATEMATIKA 2", "Математика 2", "Matematika 2"))); + subjects.add(new Subject("Programiranje 1", Set.of("Programiranje 1", "Програмирање 1", "програмирање 1", "PROGRAMIRANJE 1", "ПРОГРАМИРАЊЕ 1", "programiranje 1"))); + subjects.add(new Subject("Uvod u elektroniku", Set.of("увод у електронику", "Uvod u elektroniku", "uvod u elektroniku", "УВОД У ЕЛЕКТРОНИКУ", "Увод у електронику", "UVOD U ELEKTRONIKU"))); + subjects.add(new Subject("Osnovi komunikacija i teorija informacija", Set.of("OSNOVI KOMUNIKACIJA I TEORIJA INFORMACIJA", "Osnovi komunikacija i teorija informacija", "основи комуникација и теорија информација", "ОСНОВИ КОМУНИКАЦИЈА И ТЕОРИЈА ИНФОРМАЦИЈА", "Основи комуникација и теорија информација", "osnovi komunikacija i teorija informacija"))); + subjects.add(new Subject("Engleski jezik 1", Set.of("engleski jezik 1", "ENGLESKI JEZIK 1", "енглески језик 1", "ЕНГЛЕСКИ ЈЕЗИК 1", "Engleski jezik 1", "Енглески језик 1"))); + subjects.add(new Subject("Multimedijalne telekomunikacije", Set.of("Мултимедијалне телекомуникације", "мултимедијалне телекомуникације", "multimedijalne telekomunikacije", "Multimedijalne telekomunikacije", "MULTIMEDIJALNE TELEKOMUNIKACIJE", "МУЛТИМЕДИЈАЛНЕ ТЕЛЕКОМУНИКАЦИЈЕ"))); + subjects.add(new Subject("Internet programiranje", Set.of("internet programiranje", "INTERNET PROGRAMIRANJE", "интернет програмирање", "Интернет програмирање", "ИНТЕРНЕТ ПРОГРАМИРАЊЕ", "Internet programiranje"))); + subjects.add(new Subject("Upravljanje projektima", Set.of("управљање пројектима", "upravljanje projektima", "УПРАВЉАЊЕ ПРОЈЕКТИМА", "Управљање пројектима", "Upravljanje projektima", "UPRAVLJANJE PROJEKTIMA"))); + subjects.add(new Subject("Internet tehnologije", Set.of("ИНТЕРНЕТ ТЕХНОЛОГИЈЕ", "internet tehnologije", "Internet tehnologije", "Интернет технологије", "INTERNET TEHNOLOGIJE", "интернет технологије"))); + subjects.add(new Subject("Fizika", Set.of("ФИЗИКА", "физика", "fizika", "Физика", "FIZIKA", "Fizika"))); + subjects.add(new Subject("Bežične senzorske mreže", Set.of("BEZICNE SENZORSKE MREZE", "BEŽIČNE SENZORSKE MREŽE", "bežične senzorske mreže", "Bežične senzorske mreže", "бежичне сензорске мреже", "Бежичне сензорске мреже", "Bezicne senzorske mreze", "БЕЖИЧНЕ СЕНЗОРСКЕ МРЕЖЕ", "bezicne senzorske mreze"))); + subjects.add(new Subject("Radio-relejne komunikacije", Set.of("радио-релејне комуникације", "RADIO-RELEJNE KOMUNIKACIJE", "radio-relejne komunikacije", "Радио-релејне комуникације", "РАДИО-РЕЛЕЈНЕ КОМУНИКАЦИЈЕ", "Radio-relejne komunikacije"))); + subjects.add(new Subject("Osnovi elektrotehnike 2", Set.of("основи електротехнике 2", "Основи електротехнике 2", "osnovi elektrotehnike 2", "OSNOVI ELEKTROTEHNIKE 2", "ОСНОВИ ЕЛЕКТРОТЕХНИКЕ 2", "Osnovi elektrotehnike 2"))); + subjects.add(new Subject("Sociologija", Set.of("sociologija", "социологија", "СОЦИОЛОГИЈА", "SOCIOLOGIJA", "Sociologija", "Социологија"))); + subjects.add(new Subject("Metodi vještačke inteligencije", Set.of("Методи вјештачке интелигенције", "METODI VJEŠTAČKE INTELIGENCIJE", "Metodi vjestacke inteligencije", "МЕТОДИ ВЈЕШТАЧКЕ ИНТЕЛИГЕНЦИЈЕ", "методи вјештачке интелигенције", "metodi vjestacke inteligencije", "metodi vještačke inteligencije", "METODI VJESTACKE INTELIGENCIJE", "Metodi vještačke inteligencije"))); + subjects.add(new Subject("Baze podataka", Set.of("БАЗЕ ПОДАТАКА", "Базе података", "Baze podataka", "BAZE PODATAKA", "baze podataka", "базе података"))); + subjects.add(new Subject("Optičke telekomunikacije", Set.of("Оптичке телекомуникације", "Opticke telekomunikacije", "оптичке телекомуникације", "Optičke telekomunikacije", "OPTIČKE TELEKOMUNIKACIJE", "ОПТИЧКЕ ТЕЛЕКОМУНИКАЦИЈЕ", "optičke telekomunikacije", "opticke telekomunikacije", "OPTICKE TELEKOMUNIKACIJE"))); + subjects.add(new Subject("Stohastički sistemi i estimacija", Set.of("стохастички системи и естимација", "Stohasticki sistemi i estimacija", "Stohastički sistemi i estimacija", "Стохастички системи и естимација", "stohasticki sistemi i estimacija", "stohastički sistemi i estimacija", "STOHASTICKI SISTEMI I ESTIMACIJA", "СТОХАСТИЧКИ СИСТЕМИ И ЕСТИМАЦИЈА", "STOHASTIČKI SISTEMI I ESTIMACIJA"))); + subjects.add(new Subject("Projektovanje softvera", Set.of("projektovanje softvera", "ПРОЈЕКТОВАЊЕ СОФТВЕРА", "Пројектовање софтвера", "пројектовање софтвера", "PROJEKTOVANJE SOFTVERA", "Projektovanje softvera"))); + subjects.add(new Subject("Ugrađeni računarski sistemi", Set.of("Ugrađeni računarski sistemi", "Уграђени рачунарски системи", "ugradjeni racunarski sistemi", "Ugradjeni racunarski sistemi", "UGRADJENI RACUNARSKI SISTEMI", "уграђени рачунарски системи", "UGRAĐENI RAČUNARSKI SISTEMI", "ugrađeni računarski sistemi", "УГРАЂЕНИ РАЧУНАРСКИ СИСТЕМИ"))); + subjects.add(new Subject("Filozofija", Set.of("FILOZOFIJA", "Filozofija", "филозофија", "filozofija", "Филозофија", "ФИЛОЗОФИЈА"))); + subjects.add(new Subject("Programiranje 2", Set.of("ПРОГРАМИРАЊЕ 2", "programiranje 2", "PROGRAMIRANJE 2", "Programiranje 2", "Програмирање 2", "програмирање 2"))); + subjects.add(new Subject("Linearna elektronika", Set.of("ЛИНЕАРНА ЕЛЕКТРОНИКА", "linearna elektronika", "Линеарна електроника", "Linearna elektronika", "LINEARNA ELEKTRONIKA", "линеарна електроника"))); + subjects.add(new Subject("Analiza elektroenergetskih sistema 2", Set.of("analiza elektroenergetskih sistema 2", "АНАЛИЗА ЕЛЕКТРОЕНЕРГЕТСКИХ СИСТЕМА 2", "анализа електроенергетских система 2", "ANALIZA ELEKTROENERGETSKIH SISTEMA 2", "Анализа електроенергетских система 2", "Analiza elektroenergetskih sistema 2"))); + subjects.add(new Subject("Elektrane", Set.of("Elektrane", "elektrane", "ELEKTRANE", "електране", "ЕЛЕКТРАНЕ", "Електране"))); + subjects.add(new Subject("Računarska grafika", Set.of("Računarska grafika", "Рачунарска графика", "Racunarska grafika", "racunarska grafika", "RAČUNARSKA GRAFIKA", "рачунарска графика", "RACUNARSKA GRAFIKA", "РАЧУНАРСКА ГРАФИКА", "računarska grafika"))); + subjects.add(new Subject("Energetska elektronika", Set.of("Energetska elektronika", "energetska elektronika", "Енергетска електроника", "ENERGETSKA ELEKTRONIKA", "ЕНЕРГЕТСКА ЕЛЕКТРОНИКА", "енергетска електроника"))); + subjects.add(new Subject("Sistemi za digitalnu obradu signala", Set.of("Sistemi za digitalnu obradu signala", "sistemi za digitalnu obradu signala", "Системи за дигиталну обраду сигнала", "СИСТЕМИ ЗА ДИГИТАЛНУ ОБРАДУ СИГНАЛА", "SISTEMI ZA DIGITALNU OBRADU SIGNALA", "системи за дигиталну обраду сигнала"))); + subjects.add(new Subject("Projektovanje izvora napajanja", Set.of("projektovanje izvora napajanja", "Projektovanje izvora napajanja", "пројектовање извора напајања", "Пројектовање извора напајања", "PROJEKTOVANJE IZVORA NAPAJANJA", "ПРОЈЕКТОВАЊЕ ИЗВОРА НАПАЈАЊА"))); + subjects.add(new Subject("Digitalne telekomunikacije", Set.of("Digitalne telekomunikacije", "ДИГИТАЛНЕ ТЕЛЕКОМУНИКАЦИЈЕ", "дигиталне телекомуникације", "DIGITALNE TELEKOMUNIKACIJE", "Дигиталне телекомуникације", "digitalne telekomunikacije"))); + subjects.add(new Subject("Osnovi operativnih sistema", Set.of("OSNOVI OPERATIVNIH SISTEMA", "ОСНОВИ ОПЕРАТИВНИХ СИСТЕМА", "osnovi operativnih sistema", "Osnovi operativnih sistema", "Основи оперативних система", "основи оперативних система"))); + subjects.add(new Subject("Informacioni sistemi", Set.of("informacioni sistemi", "информациони системи", "ИНФОРМАЦИОНИ СИСТЕМИ", "Информациони системи", "INFORMACIONI SISTEMI", "Informacioni sistemi"))); + subjects.add(new Subject("Računarski integrisana proizvodnja", Set.of("рачунарски интегрисана производња", "Računarski integrisana proizvodnja", "RAČUNARSKI INTEGRISANA PROIZVODNJA", "računarski integrisana proizvodnja", "racunarski integrisana proizvodnja", "RACUNARSKI INTEGRISANA PROIZVODNJA", "Racunarski integrisana proizvodnja", "Рачунарски интегрисана производња", "РАЧУНАРСКИ ИНТЕГРИСАНА ПРОИЗВОДЊА"))); + subjects.add(new Subject("Industrijske komunikacione mreže", Set.of("Industrijske komunikacione mreze", "ИНДУСТРИЈСКЕ КОМУНИКАЦИОНЕ МРЕЖЕ", "INDUSTRIJSKE KOMUNIKACIONE MREŽE", "industrijske komunikacione mreze", "INDUSTRIJSKE KOMUNIKACIONE MREZE", "Industrijske komunikacione mreže", "industrijske komunikacione mreže", "индустријске комуникационе мреже", "Индустријске комуникационе мреже"))); + subjects.add(new Subject("Osnovi elektrotehnike 1", Set.of("osnovi elektrotehnike 1", "Основи електротехнике 1", "основи електротехнике 1", "ОСНОВИ ЕЛЕКТРОТЕХНИКЕ 1", "OSNOVI ELEKTROTEHNIKE 1", "Osnovi elektrotehnike 1"))); + subjects.add(new Subject("Inženjering softverskih zahtjeva", Set.of("Инжењеринг софтверских захтјева", "Inženjering softverskih zahtjeva", "ИНЖЕЊЕРИНГ СОФТВЕРСКИХ ЗАХТЈЕВА", "инжењеринг софтверских захтјева", "INZENJERING SOFTVERSKIH ZAHTJEVA", "INŽENJERING SOFTVERSKIH ZAHTJEVA", "inženjering softverskih zahtjeva", "inzenjering softverskih zahtjeva", "Inzenjering softverskih zahtjeva"))); + subjects.add(new Subject("Interakcija čovjek – računar", Set.of("interakcija covjek – racunar", "ИНТЕРАКЦИЈА ЧОВЈЕК – РАЧУНАР", "Интеракција човјек – рачунар", "Interakcija čovjek – računar", "интеракција човјек – рачунар", "INTERAKCIJA ČOVJEK – RAČUNAR", "Interakcija covjek – racunar", "INTERAKCIJA COVJEK – RACUNAR", "interakcija čovjek – računar"))); + subjects.add(new Subject("Umreženi sistemi upravljanja", Set.of("Umrezeni sistemi upravljanja", "Умрежени системи управљања", "Umreženi sistemi upravljanja", "umreženi sistemi upravljanja", "умрежени системи управљања", "umrezeni sistemi upravljanja", "УМРЕЖЕНИ СИСТЕМИ УПРАВЉАЊА", "UMREZENI SISTEMI UPRAVLJANJA", "UMREŽENI SISTEMI UPRAVLJANJA"))); + subjects.add(new Subject("Regulacija elektromotornih pogona", Set.of("регулација електромоторних погона", "Регулација електромоторних погона", "regulacija elektromotornih pogona", "РЕГУЛАЦИЈА ЕЛЕКТРОМОТОРНИХ ПОГОНА", "REGULACIJA ELEKTROMOTORNIH POGONA", "Regulacija elektromotornih pogona"))); + subjects.add(new Subject("Sistemi sa bazama podataka", Set.of("SISTEMI SA BAZAMA PODATAKA", "sistemi sa bazama podataka", "Системи са базама података", "системи са базама података", "СИСТЕМИ СА БАЗАМА ПОДАТАКА", "Sistemi sa bazama podataka"))); + subjects.add(new Subject("Digitalni sistemi upravljanja", Set.of("DIGITALNI SISTEMI UPRAVLJANJA", "ДИГИТАЛНИ СИСТЕМИ УПРАВЉАЊА", "дигитални системи управљања", "digitalni sistemi upravljanja", "Дигитални системи управљања", "Digitalni sistemi upravljanja"))); + subjects.add(new Subject("Prepoznavanje uzoraka", Set.of("ПРЕПОЗНАВАЊЕ УЗОРАКА", "препознавање узорака", "prepoznavanje uzoraka", "Prepoznavanje uzoraka", "Препознавање узорака", "PREPOZNAVANJE UZORAKA"))); + subjects.add(new Subject("Matematika 1", Set.of("matematika 1", "математика 1", "МАТЕМАТИКА 1", "MATEMATIKA 1", "Математика 1", "Matematika 1"))); + subjects.add(new Subject("Mikrokontrolerski sistemi", Set.of("Mikrokontrolerski sistemi", "МИКРОКОНТРОЛЕРСКИ СИСТЕМИ", "mikrokontrolerski sistemi", "MIKROKONTROLERSKI SISTEMI", "Микроконтролерски системи", "микроконтролерски системи"))); + subjects.add(new Subject("Testiranje i kvalitet softvera", Set.of("testiranje i kvalitet softvera", "TESTIRANJE I KVALITET SOFTVERA", "тестирање и квалитет софтвера", "Testiranje i kvalitet softvera", "Тестирање и квалитет софтвера", "ТЕСТИРАЊЕ И КВАЛИТЕТ СОФТВЕРА"))); + subjects.add(new Subject("Programski jezici 1", Set.of("програмски језици 1", "programski jezici 1", "Programski jezici 1", "PROGRAMSKI JEZICI 1", "ПРОГРАМСКИ ЈЕЗИЦИ 1", "Програмски језици 1"))); + subjects.add(new Subject("Osnovi radarskih sistema", Set.of("Основи радарских система", "основи радарских система", "osnovi radarskih sistema", "OSNOVI RADARSKIH SISTEMA", "Osnovi radarskih sistema", "ОСНОВИ РАДАРСКИХ СИСТЕМА"))); + subjects.add(new Subject("Impulsna elektronika", Set.of("Импулсна електроника", "ИМПУЛСНА ЕЛЕКТРОНИКА", "импулсна електроника", "Impulsna elektronika", "impulsna elektronika", "IMPULSNA ELEKTRONIKA"))); + subjects.add(new Subject("Analogna integrisana kola", Set.of("аналогна интегрисана кола", "АНАЛОГНА ИНТЕГРИСАНА КОЛА", "analogna integrisana kola", "Аналогна интегрисана кола", "Analogna integrisana kola", "ANALOGNA INTEGRISANA KOLA"))); + subjects.add(new Subject("Obnovljivi izvori energije", Set.of("ОБНОВЉИВИ ИЗВОРИ ЕНЕРГИЈЕ", "obnovljivi izvori energije", "обновљиви извори енергије", "OBNOVLJIVI IZVORI ENERGIJE", "Obnovljivi izvori energije", "Обновљиви извори енергије"))); + subjects.add(new Subject("Arhitektura računara", Set.of("arhitektura računara", "Архитектура рачунара", "АРХИТЕКТУРА РАЧУНАРА", "Arhitektura računara", "ARHITEKTURA RACUNARA", "ARHITEKTURA RAČUNARA", "Arhitektura racunara", "arhitektura racunara", "архитектура рачунара"))); + subjects.add(new Subject("Sistemi za upravljanje i nadzor", Set.of("системи за управљање и надзор", "Sistemi za upravljanje i nadzor", "Системи за управљање и надзор", "sistemi za upravljanje i nadzor", "СИСТЕМИ ЗА УПРАВЉАЊЕ И НАДЗОР", "SISTEMI ZA UPRAVLJANJE I NADZOR"))); + subjects.add(new Subject("Osnovi telekomunikacija 1", Set.of("ОСНОВИ ТЕЛЕКОМУНИКАЦИЈА 1", "Основи телекомуникација 1", "основи телекомуникација 1", "Osnovi telekomunikacija 1", "OSNOVI TELEKOMUNIKACIJA 1", "osnovi telekomunikacija 1"))); + subjects.add(new Subject("Osnovi računarske tehnike", Set.of("Osnovi racunarske tehnike", "OSNOVI RAČUNARSKE TEHNIKE", "Основи рачунарске технике", "osnovi racunarske tehnike", "osnovi računarske tehnike", "основи рачунарске технике", "Osnovi računarske tehnike", "OSNOVI RACUNARSKE TEHNIKE", "ОСНОВИ РАЧУНАРСКЕ ТЕХНИКЕ"))); + subjects.add(new Subject("Kola i signali", Set.of("Kola i signali", "Кола и сигнали", "КОЛА И СИГНАЛИ", "KOLA I SIGNALI", "кола и сигнали", "kola i signali"))); + subjects.add(new Subject("Osnovi sistemskog inženjerstva", Set.of("osnovi sistemskog inženjerstva", "osnovi sistemskog inzenjerstva", "OSNOVI SISTEMSKOG INZENJERSTVA", "ОСНОВИ СИСТЕМСКОГ ИНЖЕЊЕРСТВА", "основи системског инжењерства", "Osnovi sistemskog inzenjerstva", "Основи системског инжењерства", "Osnovi sistemskog inženjerstva", "OSNOVI SISTEMSKOG INŽENJERSTVA"))); + subjects.add(new Subject("Biomedicinska elektronika", Set.of("biomedicinska elektronika", "биомедицинска електроника", "BIOMEDICINSKA ELEKTRONIKA", "Biomedicinska elektronika", "Биомедицинска електроника", "БИОМЕДИЦИНСКА ЕЛЕКТРОНИКА"))); + subjects.add(new Subject("Električna mjerenja", Set.of("ELEKTRIČNA MJERENJA", "ELEKTRICNA MJERENJA", "elektricna mjerenja", "ЕЛЕКТРИЧНА МЈЕРЕЊА", "električna mjerenja", "Електрична мјерења", "Elektricna mjerenja", "електрична мјерења", "Električna mjerenja"))); + subjects.add(new Subject("Sistemi automatskog upravljanja", Set.of("sistemi automatskog upravljanja", "Sistemi automatskog upravljanja", "Системи аутоматског управљања", "системи аутоматског управљања", "СИСТЕМИ АУТОМАТСКОГ УПРАВЉАЊА", "SISTEMI AUTOMATSKOG UPRAVLJANJA"))); + subjects.add(new Subject("Eksploatacija elektroenergetskih sistema", Set.of("eksploatacija elektroenergetskih sistema", "EKSPLOATACIJA ELEKTROENERGETSKIH SISTEMA", "Eksploatacija elektroenergetskih sistema", "експлоатација електроенергетских система", "Експлоатација електроенергетских система", "ЕКСПЛОАТАЦИЈА ЕЛЕКТРОЕНЕРГЕТСКИХ СИСТЕМА"))); + subjects.add(new Subject("Formalne metode u softverskom inženjerstvu", Set.of("FORMALNE METODE U SOFTVERSKOM INŽENJERSTVU", "formalne metode u softverskom inženjerstvu", "Formalne metode u softverskom inženjerstvu", "Formalne metode u softverskom inzenjerstvu", "formalne metode u softverskom inzenjerstvu", "формалне методе у софтверском инжењерству", "ФОРМАЛНЕ МЕТОДЕ У СОФТВЕРСКОМ ИНЖЕЊЕРСТВУ", "FORMALNE METODE U SOFTVERSKOM INZENJERSTVU", "Формалне методе у софтверском инжењерству"))); + subjects.add(new Subject("Osnovi elektronike", Set.of("ОСНОВИ ЕЛЕКТРОНИКЕ", "Основи електронике", "osnovi elektronike", "Osnovi elektronike", "OSNOVI ELEKTRONIKE", "основи електронике"))); + subjects.add(new Subject("Osnovi elektroenergetike", Set.of("Основи електроенергетике", "osnovi elektroenergetike", "OSNOVI ELEKTROENERGETIKE", "ОСНОВИ ЕЛЕКТРОЕНЕРГЕТИКЕ", "Osnovi elektroenergetike", "основи електроенергетике"))); + subjects.add(new Subject("Programski jezici 2", Set.of("Programski jezici 2", "PROGRAMSKI JEZICI 2", "програмски језици 2", "Програмски језици 2", "programski jezici 2", "ПРОГРАМСКИ ЈЕЗИЦИ 2"))); + subjects.add(new Subject("Operativni sistemi za rad u realnom vremenu", Set.of("Operativni sistemi za rad u realnom vremenu", "operativni sistemi za rad u realnom vremenu", "OPERATIVNI SISTEMI ZA RAD U REALNOM VREMENU", "Оперативни системи за рад у реалном времену", "оперативни системи за рад у реалном времену", "ОПЕРАТИВНИ СИСТЕМИ ЗА РАД У РЕАЛНОМ ВРЕМЕНУ"))); + subjects.add(new Subject("Ispitivanje električnih mašina", Set.of("ispitivanje električnih mašina", "ispitivanje elektricnih masina", "Ispitivanje električnih mašina", "испитивање електричних машина", "ISPITIVANJE ELEKTRIČNIH MAŠINA", "ИСПИТИВАЊЕ ЕЛЕКТРИЧНИХ МАШИНА", "Испитивање електричних машина", "ISPITIVANJE ELEKTRICNIH MASINA", "Ispitivanje elektricnih masina"))); + subjects.add(new Subject("Projekat iz automatike", Set.of("projekat iz automatike", "Projekat iz automatike", "Пројекат из аутоматике", "ПРОЈЕКАТ ИЗ АУТОМАТИКЕ", "пројекат из аутоматике", "PROJEKAT IZ AUTOMATIKE"))); + subjects.add(new Subject("Zaštita u elektroenergetskim sistemima", Set.of("zaštita u elektroenergetskim sistemima", "Заштита у електроенергетским системима", "Zastita u elektroenergetskim sistemima", "заштита у електроенергетским системима", "ZAŠTITA U ELEKTROENERGETSKIM SISTEMIMA", "ZASTITA U ELEKTROENERGETSKIM SISTEMIMA", "Zaštita u elektroenergetskim sistemima", "zastita u elektroenergetskim sistemima", "ЗАШТИТА У ЕЛЕКТРОЕНЕРГЕТСКИМ СИСТЕМИМА"))); + subjects.add(new Subject("Inženjersko preduzetništvo", Set.of("inzenjersko preduzetnistvo", "INŽENJERSKO PREDUZETNIŠTVO", "INZENJERSKO PREDUZETNISTVO", "Inzenjersko preduzetnistvo", "Инжењерско предузетништво", "ИНЖЕЊЕРСКО ПРЕДУЗЕТНИШТВО", "инжењерско предузетништво", "Inženjersko preduzetništvo", "inženjersko preduzetništvo"))); + subjects.add(new Subject("Teorija električnih kola", Set.of("TEORIJA ELEKTRIČNIH KOLA", "Teorija električnih kola", "Теорија електричних кола", "Teorija elektricnih kola", "TEORIJA ELEKTRICNIH KOLA", "ТЕОРИЈА ЕЛЕКТРИЧНИХ КОЛА", "теорија електричних кола", "teorija električnih kola", "teorija elektricnih kola"))); + subjects.add(new Subject("Multimedijalni sistemi", Set.of("мултимедијални системи", "Мултимедијални системи", "multimedijalni sistemi", "Multimedijalni sistemi", "MULTIMEDIJALNI SISTEMI", "МУЛТИМЕДИЈАЛНИ СИСТЕМИ"))); + subjects.add(new Subject("Osnovi digitalne obrade signala", Set.of("ОСНОВИ ДИГИТАЛНЕ ОБРАДЕ СИГНАЛА", "osnovi digitalne obrade signala", "основи дигиталне обраде сигнала", "Основи дигиталне обраде сигнала", "Osnovi digitalne obrade signala", "OSNOVI DIGITALNE OBRADE SIGNALA"))); + subjects.add(new Subject("Teorija informacija sa kodovanjem", Set.of("TEORIJA INFORMACIJA SA KODOVANJEM", "теорија информација са кодовањем", "Теорија информација са кодовањем", "ТЕОРИЈА ИНФОРМАЦИЈА СА КОДОВАЊЕМ", "teorija informacija sa kodovanjem", "Teorija informacija sa kodovanjem"))); + subjects.add(new Subject("Osnovi telekomunikacija 2", Set.of("Osnovi telekomunikacija 2", "osnovi telekomunikacija 2", "Основи телекомуникација 2", "ОСНОВИ ТЕЛЕКОМУНИКАЦИЈА 2", "основи телекомуникација 2", "OSNOVI TELEKOMUNIKACIJA 2"))); + subjects.add(new Subject("Električne instalacije i osvjetljenje", Set.of("електричне инсталације и освјетљење", "električne instalacije i osvjetljenje", "Električne instalacije i osvjetljenje", "ELEKTRICNE INSTALACIJE I OSVJETLJENJE", "ELEKTRIČNE INSTALACIJE I OSVJETLJENJE", "Електричне инсталације и освјетљење", "Elektricne instalacije i osvjetljenje", "elektricne instalacije i osvjetljenje", "ЕЛЕКТРИЧНЕ ИНСТАЛАЦИЈЕ И ОСВЈЕТЉЕЊЕ"))); + subjects.add(new Subject("Računarska elektronika", Set.of("RACUNARSKA ELEKTRONIKA", "Računarska elektronika", "Racunarska elektronika", "рачунарска електроника", "РАЧУНАРСКА ЕЛЕКТРОНИКА", "računarska elektronika", "Рачунарска електроника", "RAČUNARSKA ELEKTRONIKA", "racunarska elektronika"))); + subjects.add(new Subject("Matematika 4", Set.of("Matematika 4", "matematika 4", "МАТЕМАТИКА 4", "математика 4", "MATEMATIKA 4", "Математика 4"))); + subjects.add(new Subject("Radio komunikacije", Set.of("Радио комуникације", "RADIO KOMUNIKACIJE", "radio komunikacije", "РАДИО КОМУНИКАЦИЈЕ", "радио комуникације", "Radio komunikacije"))); + subjects.add(new Subject("Električne mašine 1", Set.of("električne mašine 1", "Електричне машине 1", "електричне машине 1", "ELEKTRIČNE MAŠINE 1", "Elektricne masine 1", "elektricne masine 1", "ELEKTRICNE MASINE 1", "Električne mašine 1", "ЕЛЕКТРИЧНЕ МАШИНЕ 1"))); + subjects.add(new Subject("Identifikacija sistema", Set.of("Идентификација система", "identifikacija sistema", "Identifikacija sistema", "идентификација система", "IDENTIFIKACIJA SISTEMA", "ИДЕНТИФИКАЦИЈА СИСТЕМА"))); + subjects.add(new Subject("Antene i prostiranje radio-talasa", Set.of("Antene i prostiranje radio-talasa", "antene i prostiranje radio-talasa", "Антене и простирање радио-таласа", "ANTENE I PROSTIRANJE RADIO-TALASA", "антене и простирање радио-таласа", "АНТЕНЕ И ПРОСТИРАЊЕ РАДИО-ТАЛАСА"))); + subjects.add(new Subject("Mobilno računarstvo", Set.of("mobilno računarstvo", "МОБИЛНО РАЧУНАРСТВО", "MOBILNO RAČUNARSTVO", "Мобилно рачунарство", "Mobilno računarstvo", "Mobilno racunarstvo", "mobilno racunarstvo", "MOBILNO RACUNARSTVO", "мобилно рачунарство"))); + subjects.add(new Subject("Projektovanje informacionih sistema u internet okruženju", Set.of("PROJEKTOVANJE INFORMACIONIH SISTEMA U INTERNET OKRUŽENJU", "projektovanje informacionih sistema u internet okruženju", "ПРОЈЕКТОВАЊЕ ИНФОРМАЦИОНИХ СИСТЕМА У ИНТЕРНЕТ ОКРУЖЕЊУ", "Пројектовање информационих система у интернет окружењу", "Projektovanje informacionih sistema u internet okruzenju", "Projektovanje informacionih sistema u internet okruženju", "пројектовање информационих система у интернет окружењу", "PROJEKTOVANJE INFORMACIONIH SISTEMA U INTERNET OKRUZENJU", "projektovanje informacionih sistema u internet okruzenju"))); + subjects.add(new Subject("Digitalna obrada slučajnih signala", Set.of("дигитална обрада случајних сигнала", "Digitalna obrada slučajnih signala", "Digitalna obrada slucajnih signala", "Дигитална обрада случајних сигнала", "ДИГИТАЛНА ОБРАДА СЛУЧАЈНИХ СИГНАЛА", "DIGITALNA OBRADA SLUČAJNIH SIGNALA", "DIGITALNA OBRADA SLUCAJNIH SIGNALA", "digitalna obrada slucajnih signala", "digitalna obrada slučajnih signala"))); + subjects.add(new Subject("Engleski jezik 2", Set.of("енглески језик 2", "Engleski jezik 2", "ENGLESKI JEZIK 2", "Енглески језик 2", "engleski jezik 2", "ЕНГЛЕСКИ ЈЕЗИК 2"))); + subjects.add(new Subject("Projektovanje integrisanih kola", Set.of("пројектовање интегрисаних кола", "PROJEKTOVANJE INTEGRISANIH KOLA", "ПРОЈЕКТОВАЊЕ ИНТЕГРИСАНИХ КОЛА", "projektovanje integrisanih kola", "Projektovanje integrisanih kola", "Пројектовање интегрисаних кола"))); + subjects.add(new Subject("Diskretna matematika", Set.of("дискретна математика", "DISKRETNA MATEMATIKA", "ДИСКРЕТНА МАТЕМАТИКА", "diskretna matematika", "Diskretna matematika", "Дискретна математика"))); + subjects.add(new Subject("Upravljanje u realnom vremenu i ugrađeni računarski sistemi", Set.of("upravljanje u realnom vremenu i ugrađeni računarski sistemi", "УПРАВЉАЊЕ У РЕАЛНОМ ВРЕМЕНУ И УГРАЂЕНИ РАЧУНАРСКИ СИСТЕМИ", "управљање у реалном времену и уграђени рачунарски системи", "upravljanje u realnom vremenu i ugradjeni racunarski sistemi", "UPRAVLJANJE U REALNOM VREMENU I UGRAĐENI RAČUNARSKI SISTEMI", "Upravljanje u realnom vremenu i ugradjeni racunarski sistemi", "Upravljanje u realnom vremenu i ugrađeni računarski sistemi", "UPRAVLJANJE U REALNOM VREMENU I UGRADJENI RACUNARSKI SISTEMI", "Управљање у реалном времену и уграђени рачунарски системи"))); + subjects.add(new Subject("Akvizicija podataka", Set.of("Аквизиција података", "AKVIZICIJA PODATAKA", "akvizicija podataka", "Akvizicija podataka", "аквизиција података", "АКВИЗИЦИЈА ПОДАТАКА"))); + subjects.add(new Subject("Sigurnost na internetu", Set.of("Сигурност на интернету", "Sigurnost na internetu", "СИГУРНОСТ НА ИНТЕРНЕТУ", "sigurnost na internetu", "SIGURNOST NA INTERNETU", "сигурност на интернету"))); + subjects.add(new Subject("Digitalna elektronika", Set.of("DIGITALNA ELEKTRONIKA", "Дигитална електроника", "дигитална електроника", "ДИГИТАЛНА ЕЛЕКТРОНИКА", "digitalna elektronika", "Digitalna elektronika"))); + subjects.add(new Subject("Analogni i digitalni filtri", Set.of("Аналогни и дигитални филтри", "Analogni i digitalni filtri", "АНАЛОГНИ И ДИГИТАЛНИ ФИЛТРИ", "аналогни и дигитални филтри", "analogni i digitalni filtri", "ANALOGNI I DIGITALNI FILTRI"))); + subjects.add(new Subject("Elektromagnetika", Set.of("ELEKTROMAGNETIKA", "Електромагнетика", "Elektromagnetika", "електромагнетика", "elektromagnetika", "ЕЛЕКТРОМАГНЕТИКА"))); + subjects.add(new Subject("RF i mikrotalasna elektronika", Set.of("РФ и микроталасна електроника", "RF i mikrotalasna elektronika", "RF I MIKROTALASNA ELEKTRONIKA", "РФ И МИКРОТАЛАСНА ЕЛЕКТРОНИКА", "рф и микроталасна електроника", "rf i mikrotalasna elektronika"))); + subjects.add(new Subject("Mikrotalasna tehnika", Set.of("микроталасна техника", "Микроталасна техника", "MIKROTALASNA TEHNIKA", "МИКРОТАЛАСНА ТЕХНИКА", "mikrotalasna tehnika", "Mikrotalasna tehnika"))); + subjects.add(new Subject("Projektovanje digitalnih sistema", Set.of("ПРОЈЕКТОВАЊЕ ДИГИТАЛНИХ СИСТЕМА", "пројектовање дигиталних система", "Projektovanje digitalnih sistema", "Пројектовање дигиталних система", "projektovanje digitalnih sistema", "PROJEKTOVANJE DIGITALNIH SISTEMA"))); + subjects.add(new Subject("Programska podrška u digitalnoj televiziji", Set.of("ПРОГРАМСКА ПОДРШКА У ДИГИТАЛНОЈ ТЕЛЕВИЗИЈИ", "Programska podrška u digitalnoj televiziji", "Програмска подршка у дигиталној телевизији", "програмска подршка у дигиталној телевизији", "PROGRAMSKA PODRSKA U DIGITALNOJ TELEVIZIJI", "PROGRAMSKA PODRŠKA U DIGITALNOJ TELEVIZIJI", "Programska podrska u digitalnoj televiziji", "programska podrska u digitalnoj televiziji", "programska podrška u digitalnoj televiziji"))); + subjects.add(new Subject("Distributivne i industrijske mreže", Set.of("дистрибутивне и индустријске мреже", "Дистрибутивне и индустријске мреже", "distributivne i industrijske mreže", "ДИСТРИБУТИВНЕ И ИНДУСТРИЈСКЕ МРЕЖЕ", "distributivne i industrijske mreze", "DISTRIBUTIVNE I INDUSTRIJSKE MREZE", "Distributivne i industrijske mreže", "DISTRIBUTIVNE I INDUSTRIJSKE MREŽE", "Distributivne i industrijske mreze"))); + subjects.add(new Subject("Automatizacija i upravljanje u zgradama", Set.of("AUTOMATIZACIJA I UPRAVLJANJE U ZGRADAMA", "Аутоматизација и управљање у зградама", "automatizacija i upravljanje u zgradama", "АУТОМАТИЗАЦИЈА И УПРАВЉАЊЕ У ЗГРАДАМА", "Automatizacija i upravljanje u zgradama", "аутоматизација и управљање у зградама"))); + subjects.add(new Subject("Elektronsko poslovanje", Set.of("ELEKTRONSKO POSLOVANJE", "elektronsko poslovanje", "електронско пословање", "Електронско пословање", "ЕЛЕКТРОНСКО ПОСЛОВАЊЕ", "Elektronsko poslovanje"))); + subjects.add(new Subject("Analiza elektroenergetskih sistema 1", Set.of("analiza elektroenergetskih sistema 1", "ANALIZA ELEKTROENERGETSKIH SISTEMA 1", "анализа електроенергетских система 1", "АНАЛИЗА ЕЛЕКТРОЕНЕРГЕТСКИХ СИСТЕМА 1", "Analiza elektroenergetskih sistema 1", "Анализа електроенергетских система 1"))); + return subjects; + } +} diff --git a/src/main/java/dev/ksan/SubjectEntry.java b/src/main/java/dev/ksan/SubjectEntry.java new file mode 100644 index 0000000..c342322 --- /dev/null +++ b/src/main/java/dev/ksan/SubjectEntry.java @@ -0,0 +1,44 @@ +package dev.ksan; + +import java.util.List; + +public class SubjectEntry { + private String title; + private String date; + private String info; + private List paragraphs; + + public SubjectEntry(String title, String date, String info, List paragraphs) { + this.title = title; + this.date = date; + this.info = info; + this.paragraphs = paragraphs; + } + public String getTitle() { + return title; + } + public String getDate() { + return date; + } + public String getInfo() { + return info; + } + public List getParagraphs() { + return paragraphs; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if(o ==null) return false; + SubjectEntry subject = (SubjectEntry) o; + if(title.equals(subject.getTitle()) && date.equals(subject.getDate()) && info.equals(subject.getInfo())) { + return true; + } + return false; + } + @Override + public String toString() { + return title + " " + date + " " + info + "\n\t" + paragraphs + "\n"; + } + +} diff --git a/src/main/java/dev/ksan/Subscription.java b/src/main/java/dev/ksan/Subscription.java new file mode 100644 index 0000000..f6fa6e0 --- /dev/null +++ b/src/main/java/dev/ksan/Subscription.java @@ -0,0 +1,28 @@ +package dev.ksan; + +import java.util.ArrayList; +import java.util.List; + +public class Subscription { + private Subject subject; + private List users = new ArrayList<>(); + + public Subscription(Subject subject){ + this.subject = subject; + } + public synchronized void subscribe(User user){ + if(!users.contains(user)){ + users.add(user); + } + } + public synchronized void unsubscribe(User user){ + if(users.contains(user)){ + users.remove(user); + } + } + public synchronized void notifyUsers(SubjectEntry entry){ + for(User user : users){ + user.sendNotification(entry); + } + } +} diff --git a/src/main/java/dev/ksan/User.java b/src/main/java/dev/ksan/User.java new file mode 100644 index 0000000..3f586cb --- /dev/null +++ b/src/main/java/dev/ksan/User.java @@ -0,0 +1,118 @@ +package dev.ksan; + +import com.gargoylesoftware.htmlunit.html.HtmlEmailInput; +import org.simplejavamail.api.email.Email; +import org.simplejavamail.api.mailer.Mailer; +import org.simplejavamail.api.mailer.config.TransportStrategy; +import org.simplejavamail.email.EmailBuilder; +import org.simplejavamail.mailer.MailerBuilder; +import java.io.*; +import java.util.*; + +public class User implements Serializable { + private static Set usedIds = new HashSet<>(); + + private String id; + private String email; + private String password; + private NotificationMethod notificationMethod; + private Set subjectSet = new HashSet<>(); + + public User(String email, String password) { + this.id = generateId(); + this.email = email; + this.password = password; + this.notificationMethod = NotificationMethod.EMAIL; + } + + public void setNotificationMethod(NotificationMethod notificationMethod) { + this.notificationMethod = notificationMethod; + } + public void serializeToFile(String filename) throws IOException { + try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) { + oos.writeObject(this); + } + } + + public static User deserializeFromFile(String filename) throws IOException, ClassNotFoundException { + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) { + return (User) ois.readObject(); + } + } + public static void saveUsedIds(String filename) throws IOException { + try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) { + oos.writeObject(usedIds); + } + } + + @SuppressWarnings("unchecked") + public static void loadUsedIds(String filename) throws IOException, ClassNotFoundException { + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) { + usedIds = (Set) ois.readObject(); + } + } + + + public void sendNotification(SubjectEntry entry){ + + if(notificationMethod == NotificationMethod.EMAIL){ + sendEmail(entry); + } else if (notificationMethod == NotificationMethod.PUSH_NOTIFICATION) { + pushNotification(entry); + } + } + private void pushNotification(SubjectEntry entry){ + //TODO + } + private void sendEmail(SubjectEntry entry) { + MailService mail = new MailService(".env"); + mail.sendEmail(this.email,entry.getTitle(),entry.getParagraphs().toString()); + + } + + public Set getSubjectSet() { + return subjectSet; + } + + public void addSubject(Subject subject) { + subjectSet.add(subject); + } + public void addSubjects(Set subjects) { + this.subjectSet.addAll(subjects); + } + public boolean addSubject(String subject) { + Set subjects = new HashSet<>(); + subjects = Subject.generateSubjects(); + + System.out.println(subject); + for(Subject s : subjects){ + if(s.equals(new Subject(subject))){ + this.subjectSet.add(s); + return true; + //break; + } + } + return false; + } + public String getId() { + return id; + } + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + public void setPassword(String password) { + this.password = password; + } + private String generateId() { + String id; + do{ + id = UUID.randomUUID().toString(); + }while(usedIds.contains(id)); + usedIds.add(id); + return id; + } + +}