mirror of
https://github.com/hmalik144/Automation---ReedJobApplications.git
synced 2026-03-18 07:26:19 +00:00
Added excel file upon completion
This commit is contained in:
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -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")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
80
src/main/java/ListToExcel.kt
Normal file
80
src/main/java/ListToExcel.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
Reference in New Issue
Block a user