Added excel file upon completion

This commit is contained in:
2020-08-15 19:50:38 +01:00
parent 8e96ec6720
commit 6e12fe07c5
9 changed files with 344 additions and 165 deletions

View File

@@ -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()
)
}

View File

@@ -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")
// }
// }
// }
// }
}

View File

@@ -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<ReedJobObject>.writeToOut(){
try {
val workbook = XSSFWorkbook()
val sheet = workbook.createSheet("sheet1") // creating a blank sheet
val headerList = takeKClass<ReedJobObject>()
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 <reified T : Any> List<T>.writeToExcel() {
// try {
// val workbook = XSSFWorkbook()
// val sheet = workbook.createSheet("sheet1") // creating a blank sheet
// var rownum = 0
//
// val headerList = takeKClass<T>()
//
//
// 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 <reified T: Any> takeKClass(): List<String> {
val reflection = T::class
return reflection.members.map { it.name }
}
inline fun <reified T: Any> List<T>.getElements(): List<List<String>> {
val reflection = T::class
return this.map {
reflection.memberProperties.map { kProperty1 ->
kProperty1.get(it).toString()
}
}
}

View File

@@ -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<JobObject>()
var appliedJobsList = mutableListOf<ReedJobObject>()
var reedJobsList = listOf<ReedJobObject>()
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<String>){
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"
}
}

View File

@@ -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<ReedJobObject>?{
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
}

View File

@@ -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<ReedResponse>
@GET("search")
suspend fun getReedData(@Query("keywords") keywords: String,
@Query("locationName") locationName: String,
@Query("minimumSalary") minimumSalary: String) : Response<ReedResponse>
@GET("search")
suspend fun getReedData(@Query("keywords") keywords: String,
@Query("locationName") locationName: String,
@Query("minimumSalary") minimumSalary: String,
@Query("resultsToSkip") resultToSkip: Int) : Response<ReedResponse>
//instantiate api class
companion object{
operator fun invoke() : ReedApi{