diff --git a/.idea/artifacts/JobApplications_jar.xml b/.idea/artifacts/JobApplications_jar.xml
new file mode 100644
index 0000000..aea4083
--- /dev/null
+++ b/.idea/artifacts/JobApplications_jar.xml
@@ -0,0 +1,58 @@
+
+
+ $PROJECT_DIR$/out/artifacts/JobApplications_jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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/pom.xml b/pom.xml
index 0dba17b..82234e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,6 +75,12 @@
kotlinx-coroutines-core
1.3.3
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+ 1.3.61
+ compile
+
@@ -119,6 +125,8 @@
+ true
+ lib/
Main
diff --git a/src/main/java/JobObject.kt b/src/main/java/JobObject.kt
index 43eeaf5..5617bc0 100644
--- a/src/main/java/JobObject.kt
+++ b/src/main/java/JobObject.kt
@@ -1,3 +1,5 @@
+import api.network.responses.ReedJobObject
+import java.time.LocalDateTime
import java.util.*
data class JobObject(
@@ -10,7 +12,12 @@ data class JobObject(
var dateApplied : String? = null
){
- override fun toString(): String {
- return super.toString()
- }
+ constructor(job: ReedJobObject): this(
+ jobId = job.jobId.toString(),
+ website = job.jobUrl,
+ jobTitle = job.jobTitle,
+ location = job.locationName,
+ company = job.employerName,
+ dateApplied = LocalDateTime.now().toString()
+ )
}
\ No newline at end of file
diff --git a/src/main/java/LegacyCode.kt b/src/main/java/LegacyCode.kt
index f6d1be4..92a88d6 100644
--- a/src/main/java/LegacyCode.kt
+++ b/src/main/java/LegacyCode.kt
@@ -11,111 +11,111 @@ import kotlin.math.roundToInt
fun StartSearch(){
- //Open Chrome
- System.setProperty("webdriver.chrome.driver","C:\\Selenium\\selenium-java-3.141.59\\chromedriver_win32\\chromedriver.exe" )
- val driver = ChromeDriver()
-
- //open reed website login
- driver.get("https://www.reed.co.uk/account/signin?returnUrl=%2F#&card=signin")
- val wait = WebDriverWait(driver, 20)
-
- //wait for page to load
- val lastElementToLoad = driver.findElementById("signin-button")
- wait.until(ExpectedConditions.elementToBeClickable(lastElementToLoad))
-
- //insert credentials and sign in
- driver.findElementByXPath("//*[@id=\"Credentials_Email\"]").sendKeys(REED_USERNAME)
- driver.findElementByXPath("//*[@id=\"Credentials_Password\"]").sendKeys(REED_PASSWORD)
- lastElementToLoad.click()
-
- //wait for page to load
- val jobSearchEditText = driver.findElementByXPath("//*[@id=\"keywords\"]")
- wait.until(ExpectedConditions.elementToBeClickable(jobSearchEditText))
-
-// //submit search
- jobSearchEditText.sendKeys(REED_KEYWORDS)
- driver.findElementByXPath("//*[@id=\"location\"]").sendKeys(REED_LOCATION)
- driver.findElementByXPath("//*[@id=\"main-search\"]/div[1]/div[3]/button").click()
-
- //todo: change to wait
- Thread.sleep(1500)
-
- val ad = driver.findElementByXPath("//*[@id=\"content\"]/div[1]/div[2]/h1")
- wait.until(ExpectedConditions.elementToBeClickable(ad))
-
- //find number of pages
- val text = driver.findElementByCssSelector("div.page-counter").text /* eg. 1 - 25 of 99 jobs */
- print(text)
- val count = text.toTotalCount()
- val pages = count.getNumberOfPages()
-
- //loop through pages of search
- for (i in 1..pages){
-
- //open page by number on search
- //todo: change this url builder
- driver.get("https://www.reed.co.uk/jobs/android-developer-jobs-in-kilburn-london?pageno=$i")
- Thread.sleep(2500)
-
- //elements list of jobs on page
- val list = driver.findElementsByCssSelector("div.col-sm-12.col-md-9.col-lg-10.details")
-
- //turn list into global list job object
- list.forEach {
- val badge = it.findElement(By.cssSelector("div.badge-container"))
- //check if there is a badge element
- if (badge.isDisplayed){
- //see if applied is in badge
- val applied = badge.findElements(By.cssSelector("span.label.label-applied"))
- //if applied doesnt exist then add to global list of jobs
- if (applied.isNullOrEmpty()){
- val jobObject = it.toJobObject()
- jobsList.add(jobObject)
- }
-
- }else{
- //no badge exists so add to list of jobs declared at the top
- val jobObject = it.toJobObject()
- jobsList.add(jobObject)
- }
- }
- }
-
- //loop through the jobs collected
- jobsList.forEach{
- //open the URl
- driver.get(it.url)
-
- val title = driver.findElementByXPath("//*[@id=\"content\"]/div/div[2]/article")
- wait.until(ExpectedConditions.elementToBeClickable(title))
-
- //check for external apply element
- val applyExternal = driver.findElementsByCssSelector("span.external-app-caption")
-
- //if external apply is empty then apply for job
- if (applyExternal.isNullOrEmpty()){
-
- print(it.jobTitle + " ${it.url} \n" )
-
-
- //find apply button
- val applyNow = driver.findElementsByXPath("//*[@id=\"applyButtonSide\"]")
-
- if (!applyNow.isNullOrEmpty()){
-
- //click apply
- applyNow[1].click()
-
- try{
- val successfulApplied = driver.findElementByXPath("//*[@id=\"content\"]/div/div[1]/a")
- wait.until(ExpectedConditions.visibilityOf(successfulApplied))
- }catch (e: Exception){
- println(it.jobId + " did not apply")
- println("\n" + e.toString() + "\n")
- }
- }
- }
- }
+// //Open Chrome
+// System.setProperty("webdriver.chrome.driver","C:\\Selenium\\selenium-java-3.141.59\\chromedriver_win32\\chromedriver.exe" )
+// val driver = ChromeDriver()
+//
+// //open reed website login
+// driver.get("https://www.reed.co.uk/account/signin?returnUrl=%2F#&card=signin")
+// val wait = WebDriverWait(driver, 20)
+//
+// //wait for page to load
+// val lastElementToLoad = driver.findElementById("signin-button")
+// wait.until(ExpectedConditions.elementToBeClickable(lastElementToLoad))
+//
+// //insert credentials and sign in
+// driver.findElementByXPath("//*[@id=\"Credentials_Email\"]").sendKeys(REED_USERNAME)
+// driver.findElementByXPath("//*[@id=\"Credentials_Password\"]").sendKeys(REED_PASSWORD)
+// lastElementToLoad.click()
+//
+// //wait for page to load
+// val jobSearchEditText = driver.findElementByXPath("//*[@id=\"keywords\"]")
+// wait.until(ExpectedConditions.elementToBeClickable(jobSearchEditText))
+//
+//// //submit search
+// jobSearchEditText.sendKeys(REED_KEYWORDS)
+// driver.findElementByXPath("//*[@id=\"location\"]").sendKeys(REED_LOCATION)
+// driver.findElementByXPath("//*[@id=\"main-search\"]/div[1]/div[3]/button").click()
+//
+// //todo: change to wait
+// Thread.sleep(1500)
+//
+// val ad = driver.findElementByXPath("//*[@id=\"content\"]/div[1]/div[2]/h1")
+// wait.until(ExpectedConditions.elementToBeClickable(ad))
+//
+// //find number of pages
+// val text = driver.findElementByCssSelector("div.page-counter").text /* eg. 1 - 25 of 99 jobs */
+// print(text)
+// val count = text.toTotalCount()
+// val pages = count.getNumberOfPages()
+//
+// //loop through pages of search
+// for (i in 1..pages){
+//
+// //open page by number on search
+// //todo: change this url builder
+// driver.get("https://www.reed.co.uk/jobs/android-developer-jobs-in-kilburn-london?pageno=$i")
+// Thread.sleep(2500)
+//
+// //elements list of jobs on page
+// val list = driver.findElementsByCssSelector("div.col-sm-12.col-md-9.col-lg-10.details")
+//
+// //turn list into global list job object
+// list.forEach {
+// val badge = it.findElement(By.cssSelector("div.badge-container"))
+// //check if there is a badge element
+// if (badge.isDisplayed){
+// //see if applied is in badge
+// val applied = badge.findElements(By.cssSelector("span.label.label-applied"))
+// //if applied doesnt exist then add to global list of jobs
+// if (applied.isNullOrEmpty()){
+// val jobObject = it.toJobObject()
+// jobsList.add(jobObject)
+// }
+//
+// }else{
+// //no badge exists so add to list of jobs declared at the top
+// val jobObject = it.toJobObject()
+// jobsList.add(jobObject)
+// }
+// }
+// }
+//
+// //loop through the jobs collected
+// jobsList.forEach{
+// //open the URl
+// driver.get(it.url)
+//
+// val title = driver.findElementByXPath("//*[@id=\"content\"]/div/div[2]/article")
+// wait.until(ExpectedConditions.elementToBeClickable(title))
+//
+// //check for external apply element
+// val applyExternal = driver.findElementsByCssSelector("span.external-app-caption")
+//
+// //if external apply is empty then apply for job
+// if (applyExternal.isNullOrEmpty()){
+//
+// print(it.jobTitle + " ${it.url} \n" )
+//
+//
+// //find apply button
+// val applyNow = driver.findElementsByXPath("//*[@id=\"applyButtonSide\"]")
+//
+// if (!applyNow.isNullOrEmpty()){
+//
+// //click apply
+// applyNow[1].click()
+//
+// try{
+// val successfulApplied = driver.findElementByXPath("//*[@id=\"content\"]/div/div[1]/a")
+// wait.until(ExpectedConditions.visibilityOf(successfulApplied))
+// }catch (e: Exception){
+// println(it.jobId + " did not apply")
+// println("\n" + e.toString() + "\n")
+// }
+// }
+// }
+// }
}
diff --git a/src/main/java/ListToExcel.kt b/src/main/java/ListToExcel.kt
new file mode 100644
index 0000000..d919902
--- /dev/null
+++ b/src/main/java/ListToExcel.kt
@@ -0,0 +1,80 @@
+import api.network.responses.ReedJobObject
+import org.apache.poi.xssf.usermodel.XSSFWorkbook
+import java.awt.Desktop
+import java.io.File
+import java.io.FileOutputStream
+import java.time.LocalDate
+import kotlin.reflect.full.memberProperties
+
+
+fun List.writeToOut(){
+ try {
+ val workbook = XSSFWorkbook()
+ val sheet = workbook.createSheet("sheet1") // creating a blank sheet
+
+ val headerList = takeKClass()
+ val topRow = sheet.createRow(0)
+ ReedJobObject::class.memberProperties.forEachIndexed { headerPos, item ->
+ topRow.createCell(headerPos).setCellValue(headerList[headerPos])
+ }
+
+ this.forEachIndexed { index, reedJobObject ->
+ val row = sheet.createRow(index + 1)
+
+ ReedJobObject::class.memberProperties.forEachIndexed { headerPos, item ->
+ row.createCell(headerPos).setCellValue(item.get(reedJobObject).toString())
+ }
+ }
+
+ val dateTime = LocalDate.now().toString()
+ val file = File("E:\\Reed search output\\Jobs applied - ${dateTime}.xlsx")
+ val out = FileOutputStream(file) // file name with path
+ workbook.write(out)
+ out.close()
+ val desktop = Desktop.getDesktop()
+ if (file.exists()) //checks file exists or not
+ desktop.open(file) //opens the specified file
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+}
+
+//inline fun List.writeToExcel() {
+// try {
+// val workbook = XSSFWorkbook()
+// val sheet = workbook.createSheet("sheet1") // creating a blank sheet
+// var rownum = 0
+//
+// val headerList = takeKClass()
+//
+//
+// for (user in this) {
+//
+// val row: Row = sheet.createRow(rownum++)
+// createList(user, row)
+// }
+// val out = FileOutputStream(File("NewFile.xlsx")) // file name with path
+// workbook.write(out)
+// out.close()
+// } catch (e: Exception) {
+// e.printStackTrace()
+// }
+//}
+
+
+inline fun takeKClass(): List {
+ val reflection = T::class
+
+ return reflection.members.map { it.name }
+}
+
+inline fun List.getElements(): List> {
+ val reflection = T::class
+
+ return this.map {
+ reflection.memberProperties.map { kProperty1 ->
+ kProperty1.get(it).toString()
+ }
+ }
+
+}
diff --git a/src/main/java/Main.kt b/src/main/java/Main.kt
index cf8aa40..c049d94 100644
--- a/src/main/java/Main.kt
+++ b/src/main/java/Main.kt
@@ -4,72 +4,78 @@ import api.network.NetworkRequests
import api.network.responses.ReedJobObject
import org.openqa.selenium.JavascriptExecutor
import org.openqa.selenium.chrome.ChromeDriver
-import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
+import kotlin.system.exitProcess
-var jobsList = mutableListOf()
+var appliedJobsList = mutableListOf()
var reedJobsList = listOf()
lateinit var driver: ChromeDriver
lateinit var wait: WebDriverWait
+const val driverPath = "C:\\Selenium\\selenium-java-3.141.59\\chromedriver_win32\\chromedriver.exe"
public fun main(args: Array){
- setup()
+ getJobsFromReedApi()
setupWebDriver()
logonToReed()
- applyForJobsThroughLoop()
+ applyForJobsThroughLoop {
+ appliedJobsList.writeToOut()
+ driver.close()
+ exitProcess(2)
+ }
}
-fun setup(){
+fun getJobsFromReedApi(){
val result = NetworkRequests().getSearchApi()
-
- if (!result.isNullOrEmpty()){
- reedJobsList = result
+ result?.let {
+ reedJobsList = it
+ return
}
-
+ // No results found so exit
+ exitProcess(2)
}
fun setupWebDriver(){
//Open Chrome
- System.setProperty("webdriver.chrome.driver","C:\\Selenium\\selenium-java-3.141.59\\chromedriver_win32\\chromedriver.exe" )
+ System.setProperty("webdriver.chrome.driver", driverPath)
driver = ChromeDriver()
wait = WebDriverWait(driver, 20)
}
-fun applyForJobsThroughLoop(){
-
- reedJobsList.forEach {
- try {
- driver.get(it.jobUrl)
- applyForJob(it)
-
- }catch (e: Exception){
- println("\n" + e.toString() + "\n")
- }
- }
-}
-
fun logonToReed(){
driver.get("https://www.reed.co.uk/account/signin?returnUrl=%2F#&card=signin")
-
- //wait for page to load
- val lastElementToLoad = driver.findElementById("signin-button")
- wait.until(ExpectedConditions.elementToBeClickable(lastElementToLoad))
+ waitForPageToLoad()
//insert credentials and sign in
driver.findElementByXPath("//*[@id=\"Credentials_Email\"]").sendKeys(REED_USERNAME)
driver.findElementByXPath("//*[@id=\"Credentials_Password\"]").sendKeys(REED_PASSWORD)
- lastElementToLoad.click()
- //wait for page to load
- val jobSearchEditText = driver.findElementByXPath("//*[@id=\"keywords\"]")
- wait.until(ExpectedConditions.elementToBeClickable(jobSearchEditText))
+ // Click login and wait for page to load
+ driver.findElementById("signin-button").click()
+ waitForPageToLoad()
+}
+
+fun applyForJobsThroughLoop(
+ complete: () -> Unit
+){
+ reedJobsList.forEach {
+ try {
+ // load url
+ driver.get(it.jobUrl)
+ // load [age
+ waitForPageToLoad()
+ // apply for job
+ applyForJob(it)
+ }catch (e: Exception){
+ e.printStackTrace()
+ }
+ }
+ complete()
}
fun applyForJob(jobObject: ReedJobObject){
- val appliedBefore = driver.findElementsByXPath("//*[@id=\"content\"]/div/div[2]/article/div/div[1]/div")
- if (appliedBefore.isNullOrEmpty()){
+ if (hasUserNotAppliedBefore()){
println("${jobObject.jobId} has not been applied")
//find apply button
val applyNow = driver.findElementsByXPath("//*[@id=\"applyButtonSide\"]")
@@ -78,22 +84,27 @@ fun applyForJob(jobObject: ReedJobObject){
//click apply
val index = if (applyNow.size > 1){ 1 }else{ 0 }
applyNow[index].click()
-
- try{
-// val successfulApplied = driver.findElementByCssSelector("div.alert.alert-success alert-borderless")
- wait.until{
- driver.executeScript("return document.readyState") == "complete"
- }
-
- }catch (e: Exception){
- println("\n" + e.toString() + "\n")
- }
+ waitForPageToLoad()
+ println("${jobObject.jobId} has now been applied for")
+ appliedJobsList.add(jobObject)
+ return
}
+ println("${jobObject.jobId} could not be applied for")
}else{
- println("${jobObject.jobId} has been applied")
+ println("${jobObject.jobId} has been applied for previously")
}
-
-
-
}
+// Checks to see if user has applied before
+fun hasUserNotAppliedBefore(): Boolean{
+ // find "applied before" text
+ val appliedBefore = driver.findElementsByXPath("//*[@id=\"content\"]/div/div[2]/article/div/div[1]/div")
+ return appliedBefore.isNullOrEmpty()
+}
+
+fun waitForPageToLoad(){
+ //wait for page to load
+ wait.until{driv ->
+ (driv as JavascriptExecutor).executeScript("return document.readyState") == "complete"
+ }
+}
diff --git a/src/main/java/api/network/NetworkRequests.kt b/src/main/java/api/network/NetworkRequests.kt
index ce3c3ae..5faa69b 100644
--- a/src/main/java/api/network/NetworkRequests.kt
+++ b/src/main/java/api/network/NetworkRequests.kt
@@ -1,30 +1,29 @@
package api.network
import Constants.Companion.REED_KEYWORDS
-import Constants.Companion.REED_LOCATION
import Constants.Companion.REED_MINIMUM_SALARY
import api.network.responses.ReedJobObject
import com.example.h_mal.androiddevelopertechtest_incrowdsports.data.network.ReedApi
import com.example.h_mal.androiddevelopertechtest_incrowdsports.data.network.SafeApiRequest
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class NetworkRequests : SafeApiRequest(){
- val reedApi = ReedApi()
+ val reedApi by lazy { ReedApi() }
fun getSearchApi() : List?{
try {
- val response = runBlocking { apiRequest { reedApi.getGameData(REED_KEYWORDS, REED_LOCATION, REED_MINIMUM_SALARY) } }
+ // get results from Api
+ val response = runBlocking {
+ apiRequest { reedApi.getReedData(REED_KEYWORDS, REED_MINIMUM_SALARY) }
+ }
+ // check we have results
response.results?.let {
return it
}
-
}catch (e : Exception){
- println("*** $e")
+ e.printStackTrace()
}
return null
}
diff --git a/src/main/java/api/network/ReedApi.kt b/src/main/java/api/network/ReedApi.kt
index 763dd31..28bfc3e 100644
--- a/src/main/java/api/network/ReedApi.kt
+++ b/src/main/java/api/network/ReedApi.kt
@@ -14,12 +14,22 @@ import retrofit2.http.Query
interface ReedApi {
- //get the game data from api call of relevent gameID
+ //get the job data from api call to Reed api data service
@GET("search")
- suspend fun getGameData(@Query("keywords") keywords: String,
+ suspend fun getReedData(@Query("keywords") keywords: String,
+ @Query("minimumSalary") minimumSalary: String) : Response
+
+ @GET("search")
+ suspend fun getReedData(@Query("keywords") keywords: String,
@Query("locationName") locationName: String,
@Query("minimumSalary") minimumSalary: String) : Response
+ @GET("search")
+ suspend fun getReedData(@Query("keywords") keywords: String,
+ @Query("locationName") locationName: String,
+ @Query("minimumSalary") minimumSalary: String,
+ @Query("resultsToSkip") resultToSkip: Int) : Response
+
//instantiate api class
companion object{
operator fun invoke() : ReedApi{