diff --git a/src/main/java/com/techlooper/cron/VietnamworksJobImporter.java b/src/main/java/com/techlooper/cron/VietnamworksJobImporter.java index 660d2aa5c..13d47748f 100644 --- a/src/main/java/com/techlooper/cron/VietnamworksJobImporter.java +++ b/src/main/java/com/techlooper/cron/VietnamworksJobImporter.java @@ -1,5 +1,7 @@ package com.techlooper.cron; +import com.techlooper.model.JobCrawlerSourceEnum; +import com.techlooper.model.JobSearchCriteria; import com.techlooper.model.JobTypeEnum; import com.techlooper.service.JobAggregatorService; import org.slf4j.Logger; @@ -35,6 +37,10 @@ public void indexJobFromVietnamworks() throws Exception { int numberOfNormalJobs = jobAggregatorService.importVietnamworksJob(JobTypeEnum.NORMAL); LOGGER.info(numberOfTopPriorityJobs + " top priority jobs has been indexed already."); LOGGER.info(numberOfNormalJobs + " normal jobs has been indexed already."); + + JobSearchCriteria criteria = new JobSearchCriteria(); + criteria.setCrawlSource(JobCrawlerSourceEnum.VIETNAMWORKS.getValue()); + jobAggregatorService.updateJobExpiration(criteria); } catch (Throwable ex) { LOGGER.error(ex.getMessage(), ex); } diff --git a/src/main/java/com/techlooper/entity/JobEntity.java b/src/main/java/com/techlooper/entity/JobEntity.java index 7e425b042..078a6cae2 100644 --- a/src/main/java/com/techlooper/entity/JobEntity.java +++ b/src/main/java/com/techlooper/entity/JobEntity.java @@ -9,6 +9,7 @@ import java.util.List; +import static org.springframework.data.elasticsearch.annotations.FieldType.Integer; import static org.springframework.data.elasticsearch.annotations.FieldType.Long; import static org.springframework.data.elasticsearch.annotations.FieldType.String; @@ -36,6 +37,9 @@ public class JobEntity { @Field(type = FieldType.Nested) private List skills; + @Field(type = Integer) + private Integer isActive; + public String getId() { return id; } @@ -84,6 +88,14 @@ public void setSkills(List skills) { this.skills = skills; } + public Integer getIsActive() { + return isActive; + } + + public void setIsActive(Integer isActive) { + this.isActive = isActive; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/com/techlooper/entity/ScrapeJobEntity.java b/src/main/java/com/techlooper/entity/ScrapeJobEntity.java index be903d6b0..17b167217 100644 --- a/src/main/java/com/techlooper/entity/ScrapeJobEntity.java +++ b/src/main/java/com/techlooper/entity/ScrapeJobEntity.java @@ -56,6 +56,9 @@ public class ScrapeJobEntity { @Field(type = Nested) private List skills; + @Field(type = Integer) + private Integer isActive; + public String getJobId() { return jobId; } @@ -167,4 +170,12 @@ public List getSkills() { public void setSkills(List skills) { this.skills = skills; } + + public Integer getIsActive() { + return isActive; + } + + public void setIsActive(Integer isActive) { + this.isActive = isActive; + } } diff --git a/src/main/java/com/techlooper/model/JobCrawlerSourceEnum.java b/src/main/java/com/techlooper/model/JobCrawlerSourceEnum.java new file mode 100644 index 000000000..8d53d5f66 --- /dev/null +++ b/src/main/java/com/techlooper/model/JobCrawlerSourceEnum.java @@ -0,0 +1,16 @@ +package com.techlooper.model; + +public enum JobCrawlerSourceEnum { + + VIETNAMWORKS("vietnamworks"); + + private String value; + + private JobCrawlerSourceEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/com/techlooper/model/JobResponse.java b/src/main/java/com/techlooper/model/JobResponse.java index b8faa0596..f1de1cdb0 100644 --- a/src/main/java/com/techlooper/model/JobResponse.java +++ b/src/main/java/com/techlooper/model/JobResponse.java @@ -2,7 +2,6 @@ import com.techlooper.entity.CompanyBenefit; -import java.util.Collection; import java.util.List; /** @@ -10,239 +9,249 @@ */ public class JobResponse { - private String url; + private String jobId; - private String title; + private String url; - private String location; + private String title; - private String level; + private String location; - private String postedOn; + private String level; - private String company; + private String postedOn; - private String videoUrl; + private String company; - private String logoUrl; + private String videoUrl; - private String salary; + private String logoUrl; - private Long salaryMin; + private String salary; - private Long salaryMax; + private Long salaryMin; - private Boolean topPriority; + private Long salaryMax; - private List benefits; + private Boolean topPriority; - private List skills; + private List benefits; + + private List skills; // public static Collection topPriorityJobIds; - public static class Builder { + public static class Builder { + + private JobResponse instance = new JobResponse(); + + public Builder withUrl(String url) { + instance.url = url; + return this; + } + + public Builder withTitle(String title) { + instance.title = title; + return this; + } + + public Builder withLocation(String location) { + instance.location = location; + return this; + } + + public Builder withLevel(String level) { + instance.level = level; + return this; + } + + public Builder withPostedOn(String postedOn) { + instance.postedOn = postedOn; + return this; + } + + public Builder withCompany(String company) { + instance.company = company; + return this; + } + + public Builder withVideoUrl(String videoUrl) { + instance.videoUrl = videoUrl; + return this; + } + + public Builder withLogoUrl(String logoUrl) { + instance.logoUrl = logoUrl; + return this; + } + + public Builder withSalary(String salary) { + instance.salary = salary; + return this; + } + + public Builder withSalaryMin(Long salaryMin) { + instance.salaryMin = salaryMin; + return this; + } + + public Builder withSalaryMax(Long salaryMax) { + instance.salaryMax = salaryMax; + return this; + } + + public Builder withTopPriority(Boolean topPriority) { + instance.setTopPriority(topPriority); + return this; + } + + public Builder withBenefits(List benefits) { + instance.benefits = benefits; + return this; + } + + public Builder withSkills(List skills) { + instance.skills = skills; + return this; + } + + public JobResponse build() { + return instance; + } + } - private JobResponse instance = new JobResponse(); + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - public Builder withUrl(String url) { - instance.url = url; - return this; - } + JobResponse that = (JobResponse) o; - public Builder withTitle(String title) { - instance.title = title; - return this; - } + if (url != null ? !url.equals(that.url) : that.url != null) return false; - public Builder withLocation(String location) { - instance.location = location; - return this; + return true; } - public Builder withLevel(String level) { - instance.level = level; - return this; + public int hashCode() { + return url != null ? url.hashCode() : 0; } - public Builder withPostedOn(String postedOn) { - instance.postedOn = postedOn; - return this; + public String getJobId() { + return jobId; } - public Builder withCompany(String company) { - instance.company = company; - return this; + public void setJobId(String jobId) { + this.jobId = jobId; } - public Builder withVideoUrl(String videoUrl) { - instance.videoUrl = videoUrl; - return this; + public String getUrl() { + return url; } - public Builder withLogoUrl(String logoUrl) { - instance.logoUrl = logoUrl; - return this; + public void setUrl(String url) { + this.url = url; } - public Builder withSalary(String salary) { - instance.salary = salary; - return this; + public String getTitle() { + return title; } - public Builder withSalaryMin(Long salaryMin) { - instance.salaryMin = salaryMin; - return this; + public void setTitle(String title) { + this.title = title; } - public Builder withSalaryMax(Long salaryMax) { - instance.salaryMax = salaryMax; - return this; + public String getLocation() { + return location; } - public Builder withTopPriority(Boolean topPriority) { - instance.setTopPriority(topPriority); - return this; + public void setLocation(String location) { + this.location = location; } - public Builder withBenefits(List benefits) { - instance.benefits = benefits; - return this; + public String getLevel() { + return level; } - public Builder withSkills(List skills) { - instance.skills = skills; - return this; + public void setLevel(String level) { + this.level = level; } - public JobResponse build() { - return instance; + public String getPostedOn() { + return postedOn; } - } - - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - JobResponse that = (JobResponse) o; - - if (url != null ? !url.equals(that.url) : that.url != null) return false; - - return true; - } - - public int hashCode() { - return url != null ? url.hashCode() : 0; - } - - public String getUrl() { - return url; - } - public void setUrl(String url) { - this.url = url; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getLocation() { - return location; - } - - public void setLocation(String location) { - this.location = location; - } - - public String getLevel() { - return level; - } - - public void setLevel(String level) { - this.level = level; - } - - public String getPostedOn() { - return postedOn; - } - - public void setPostedOn(String postedOn) { - this.postedOn = postedOn; - } + public void setPostedOn(String postedOn) { + this.postedOn = postedOn; + } - public String getCompany() { - return company; - } + public String getCompany() { + return company; + } - public void setCompany(String company) { - this.company = company; - } + public void setCompany(String company) { + this.company = company; + } - public String getVideoUrl() { - return videoUrl; - } + public String getVideoUrl() { + return videoUrl; + } - public void setVideoUrl(String videoUrl) { - this.videoUrl = videoUrl; - } + public void setVideoUrl(String videoUrl) { + this.videoUrl = videoUrl; + } - public String getLogoUrl() { - return logoUrl; - } + public String getLogoUrl() { + return logoUrl; + } - public void setLogoUrl(String logoUrl) { - this.logoUrl = logoUrl; - } + public void setLogoUrl(String logoUrl) { + this.logoUrl = logoUrl; + } - public String getSalary() { - return salary; - } + public String getSalary() { + return salary; + } - public void setSalary(String salary) { - this.salary = salary; - } + public void setSalary(String salary) { + this.salary = salary; + } - public Long getSalaryMin() { - return salaryMin; - } + public Long getSalaryMin() { + return salaryMin; + } - public void setSalaryMin(Long salaryMin) { - this.salaryMin = salaryMin; - } + public void setSalaryMin(Long salaryMin) { + this.salaryMin = salaryMin; + } - public Long getSalaryMax() { - return salaryMax; - } + public Long getSalaryMax() { + return salaryMax; + } - public void setSalaryMax(Long salaryMax) { - this.salaryMax = salaryMax; - } + public void setSalaryMax(Long salaryMax) { + this.salaryMax = salaryMax; + } - public List getBenefits() { - return benefits; - } + public List getBenefits() { + return benefits; + } - public void setBenefits(List benefits) { - this.benefits = benefits; - } + public void setBenefits(List benefits) { + this.benefits = benefits; + } - public List getSkills() { - return skills; - } + public List getSkills() { + return skills; + } - public void setSkills(List skills) { - this.skills = skills; - } + public void setSkills(List skills) { + this.skills = skills; + } - public Boolean getTopPriority() { - return topPriority; - } + public Boolean getTopPriority() { + return topPriority; + } - public void setTopPriority(Boolean topPriority) { - this.topPriority = topPriority; - } + public void setTopPriority(Boolean topPriority) { + this.topPriority = topPriority; + } } diff --git a/src/main/java/com/techlooper/model/JobSearchCriteria.java b/src/main/java/com/techlooper/model/JobSearchCriteria.java index 93b4acd78..f68308c01 100644 --- a/src/main/java/com/techlooper/model/JobSearchCriteria.java +++ b/src/main/java/com/techlooper/model/JobSearchCriteria.java @@ -13,6 +13,8 @@ public class JobSearchCriteria { private Boolean topPriority; + private String crawlSource; + public JobSearchCriteria(){ this.page = 1; } @@ -52,4 +54,12 @@ public Boolean getTopPriority() { public void setTopPriority(Boolean topPriority) { this.topPriority = topPriority; } + + public String getCrawlSource() { + return crawlSource; + } + + public void setCrawlSource(String crawlSource) { + this.crawlSource = crawlSource; + } } diff --git a/src/main/java/com/techlooper/repository/elasticsearch/VietnamworksJobRepository.java b/src/main/java/com/techlooper/repository/elasticsearch/VietnamworksJobRepository.java new file mode 100644 index 000000000..280ce9615 --- /dev/null +++ b/src/main/java/com/techlooper/repository/elasticsearch/VietnamworksJobRepository.java @@ -0,0 +1,7 @@ +package com.techlooper.repository.elasticsearch; + +import com.techlooper.entity.JobEntity; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +public interface VietnamworksJobRepository extends ElasticsearchRepository { +} diff --git a/src/main/java/com/techlooper/service/JobAggregatorService.java b/src/main/java/com/techlooper/service/JobAggregatorService.java index a5ffa2529..1bab7ee7d 100644 --- a/src/main/java/com/techlooper/service/JobAggregatorService.java +++ b/src/main/java/com/techlooper/service/JobAggregatorService.java @@ -1,7 +1,6 @@ package com.techlooper.service; import com.techlooper.entity.JobAlertRegistrationEntity; -import com.techlooper.entity.ScrapeJobEntity; import com.techlooper.model.*; import java.util.List; @@ -10,6 +9,8 @@ public interface JobAggregatorService { JobSearchResponse findJob(JobSearchCriteria criteria); + void updateJobExpiration(JobSearchCriteria criteria); + JobAlertRegistrationEntity registerJobAlert(JobAlertRegistration jobAlertRegistration) throws Exception; List findJobAlertRegistration(JobAlertPeriodEnum period) throws Exception; diff --git a/src/main/java/com/techlooper/service/JobSearchService.java b/src/main/java/com/techlooper/service/JobSearchService.java index 350577608..f2932ea0e 100644 --- a/src/main/java/com/techlooper/service/JobSearchService.java +++ b/src/main/java/com/techlooper/service/JobSearchService.java @@ -34,4 +34,8 @@ public interface JobSearchService { List getHigherSalaryJobs(SalaryReviewEntity salaryReviewEntity); Double getAverageSalary(Long salaryMin, Long salaryMax); + + JobEntity findJobById(String id); + + JobEntity updateJob(JobEntity jobEntity); } diff --git a/src/main/java/com/techlooper/service/impl/JobAggregatorServiceImpl.java b/src/main/java/com/techlooper/service/impl/JobAggregatorServiceImpl.java index eac33af1c..3a214d92c 100644 --- a/src/main/java/com/techlooper/service/impl/JobAggregatorServiceImpl.java +++ b/src/main/java/com/techlooper/service/impl/JobAggregatorServiceImpl.java @@ -1,6 +1,7 @@ package com.techlooper.service.impl; import com.techlooper.entity.JobAlertRegistrationEntity; +import com.techlooper.entity.JobEntity; import com.techlooper.entity.ScrapeJobEntity; import com.techlooper.model.*; import com.techlooper.repository.userimport.JobAlertRegistrationRepository; @@ -11,7 +12,10 @@ import freemarker.template.Template; import org.apache.commons.lang3.StringUtils; import org.dozer.Mapper; -import org.elasticsearch.index.query.*; +import org.elasticsearch.index.query.BoolFilterBuilder; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.springframework.beans.factory.annotation.Value; @@ -34,7 +38,7 @@ import static java.util.stream.Collectors.toList; import static org.elasticsearch.index.query.FilterBuilders.rangeFilter; import static org.elasticsearch.index.query.FilterBuilders.termFilter; -import static org.elasticsearch.index.query.MatchQueryBuilder.*; +import static org.elasticsearch.index.query.MatchQueryBuilder.Operator; import static org.elasticsearch.index.query.QueryBuilders.*; @Service @@ -146,20 +150,38 @@ public JobSearchResponse findJob(JobSearchCriteria criteria) { Integer totalPage = searchResult.getTotalPages(); List jobs = scrapeJobRepository.search(searchQueryBuilder.build()).getContent(); - List result = new ArrayList<>(); - if (!jobs.isEmpty()) { - for (ScrapeJobEntity jobEntity : jobs) { - JobResponse job = dozerMapper.map(jobEntity, JobResponse.class); - job.setTopPriority(jobEntity.getTopPriority() != null ? jobEntity.getTopPriority() : Boolean.FALSE); - result.add(job); - } - } + List result = getJobResponses(jobs); JobSearchResponse jobSearchResponse = new JobSearchResponse.Builder() .withTotalJob(totalJob).withTotalPage(totalPage).withPage(criteria.getPage()).withJobs(result).build(); return jobSearchResponse; } + @Override + public void updateJobExpiration(JobSearchCriteria criteria) { + NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder().withTypes("job"); + searchQueryBuilder.withQuery(termQuery("crawlSource", criteria.getCrawlSource())); + + int page = 0; + List jobs = new ArrayList<>(); + do { + searchQueryBuilder.withPageable(new PageRequest(page, 100)); + FacetedPage searchResult = scrapeJobRepository.search(searchQueryBuilder.build()); + jobs = searchResult.getContent(); + + if (!jobs.isEmpty()) { + for (ScrapeJobEntity job : jobs) { + JobEntity jobEntity = vietnamWorksJobSearchService.findJobById(job.getJobId()); + if (jobEntity != null) { + job.setIsActive(jobEntity.getIsActive()); + scrapeJobRepository.save(job); + } + } + } + page++; + } while (!jobs.isEmpty()); + } + @Override public boolean exceedJobAlertRegistrationLimit(String email) { final long CONFIGURED_JOB_ALERT_LIMIT_REGISTRATION = 5; @@ -190,6 +212,7 @@ public int importVietnamworksJob(JobTypeEnum jobType) { Boolean isTopPriorityJob = (jobType == JobTypeEnum.TOP_PRIORITY) ? Boolean.TRUE : Boolean.FALSE; VNWJobSearchResponse vnwJobSearchResponse; + int sum = 0; do { vnwJobSearchResponse = vietnamWorksJobSearchService.searchJob(jobSearchRequest); if (vnwJobSearchResponse.hasData()) { @@ -198,10 +221,11 @@ public int importVietnamworksJob(JobTypeEnum jobType) { scrapeJobRepository.save(jobEntities); jobSearchRequest.setPageNumber(jobSearchRequest.getPageNumber() + 1); jobSearchRequestBuilder.withPageNumber(jobSearchRequest.getPageNumber()); + sum += vnwJobSearchResponse.getData().getTotal(); } } while (vnwJobSearchResponse.hasData()); - return vnwJobSearchResponse != null ? vnwJobSearchResponse.getData().getTotal() : 0; + return sum; } private String buildSearchPath(JobAlertRegistrationEntity jobAlertRegistrationEntity) { @@ -240,7 +264,8 @@ private NativeSearchQueryBuilder getJobListingQueryBuilder(JobSearchCriteria cri } else { queryBuilder = boolQuery(); if (StringUtils.isNotEmpty(criteria.getKeyword())) { - ((BoolQueryBuilder) queryBuilder).must(multiMatchQuery(criteria.getKeyword(), "jobTitle", "company")); + ((BoolQueryBuilder) queryBuilder).must( + multiMatchQuery(criteria.getKeyword()).field("jobTitle", 8).field("company", 2)); } if (StringUtils.isNotEmpty(criteria.getLocation())) { ((BoolQueryBuilder) queryBuilder).must(matchQuery("location", criteria.getLocation()).operator(Operator.AND)); @@ -248,6 +273,7 @@ private NativeSearchQueryBuilder getJobListingQueryBuilder(JobSearchCriteria cri } BoolFilterBuilder filterBuilder = new BoolFilterBuilder(); + filterBuilder.mustNot(termFilter("isActive", 0)); filterBuilder.must(rangeFilter("createdDateTime").from("now-30d/d")); if (criteria.getTopPriority() != null) { if (criteria.getTopPriority() == true) { @@ -270,4 +296,16 @@ private int getJobAlertBucketNumber(JobAlertPeriodEnum period) throws Exception int numberOfDays = DateTimeUtils.daysBetween(launchDate, new Date()); return numberOfDays % period.getValue(); } + + private List getJobResponses(List jobs) { + List result = new ArrayList<>(); + if (!jobs.isEmpty()) { + for (ScrapeJobEntity jobEntity : jobs) { + JobResponse job = dozerMapper.map(jobEntity, JobResponse.class); + job.setTopPriority(jobEntity.getTopPriority() != null ? jobEntity.getTopPriority() : Boolean.FALSE); + result.add(job); + } + } + return result; + } } diff --git a/src/main/java/com/techlooper/service/impl/VietnamWorksJobSearchService.java b/src/main/java/com/techlooper/service/impl/VietnamWorksJobSearchService.java index bb2947e89..524c39c82 100644 --- a/src/main/java/com/techlooper/service/impl/VietnamWorksJobSearchService.java +++ b/src/main/java/com/techlooper/service/impl/VietnamWorksJobSearchService.java @@ -7,6 +7,7 @@ import com.techlooper.model.VNWJobSearchResponse; import com.techlooper.model.VNWJobSearchResponseDataItem; import com.techlooper.repository.JobSearchAPIConfigurationRepository; +import com.techlooper.repository.elasticsearch.VietnamworksJobRepository; import com.techlooper.service.JobQueryBuilder; import com.techlooper.service.JobSearchService; import com.techlooper.util.JsonUtils; @@ -49,194 +50,205 @@ @Service public class VietnamWorksJobSearchService implements JobSearchService { - @Resource - private RestTemplate restTemplate; - - @Resource - private JobSearchAPIConfigurationRepository apiConfiguration; - - private VNWConfigurationResponse configurationResponse; - - @Resource - private JobQueryBuilder jobQueryBuilder; - - @Resource - private ElasticsearchTemplate elasticsearchTemplate; - - private static final long VIETNAM_CURRENCY_SALARY_DETECTOR = 1000000L; - - private static final long VND_USD_RATE = 21000L; - - private static final int SORT_ORDER_ASC = 1; - - private static final int SORT_ORDER_DESC = -1; - - /** - * Get the configuration from Vietnamworks API such as job locations, categories, degree, etc - * - * @return The configuration from the API {@link com.techlooper.model.VNWConfigurationResponse} - */ - public VNWConfigurationResponse getConfiguration() { - return Optional.ofNullable(configurationResponse).orElseGet(() -> { - HttpEntity requestEntity = RestTemplateUtils.configureHttpRequestEntity( - MediaType.APPLICATION_JSON, apiConfiguration.getApiKeyName(), apiConfiguration.getApiKeyValue(), EMPTY); - ResponseEntity responseEntity = restTemplate.exchange( - apiConfiguration.getConfigurationUrl(), HttpMethod.GET, requestEntity, String.class); - final Optional configuration = Optional.ofNullable(responseEntity.getBody()); - - if (configuration.isPresent()) { - configurationResponse = JsonUtils.toPOJO(configuration.get(), VNWConfigurationResponse.class). - orElseGet(VNWConfigurationResponse::new); - } - return configurationResponse; - }); - } - - /** - * Get the job search result from Vietnamworks API which matches the criteria terms - * - * @param jobSearchRequest The job search request which contains the criteria terms and page number - * @return The job search result from the API {@link com.techlooper.model.VNWJobSearchResponse} - */ - public VNWJobSearchResponse searchJob(VNWJobSearchRequest jobSearchRequest) { - final String searchParameters = JsonUtils.toJSON(jobSearchRequest).orElse(EMPTY); - HttpEntity requestEntity = RestTemplateUtils.configureHttpRequestEntity( - MediaType.APPLICATION_JSON, apiConfiguration.getApiKeyName(), apiConfiguration.getApiKeyValue(), searchParameters); - ResponseEntity responseEntity = restTemplate.exchange( - apiConfiguration.getSearchUrl(), HttpMethod.POST, requestEntity, String.class); - - final Optional jobSearchResponseJson = Optional.ofNullable(responseEntity.getBody()); - - if (jobSearchResponseJson.isPresent()) { - final VNWJobSearchResponse actualResult = JsonUtils.toPOJO(jobSearchResponseJson.get(), VNWJobSearchResponse.class) - .orElse(VNWJobSearchResponse.getDefaultObject()); - - if (actualResult.hasData()) { - //mergeSearchResultWithConfiguration(actualResult, getConfiguration()); - return actualResult; - } + @Resource + private RestTemplate restTemplate; + + @Resource + private JobSearchAPIConfigurationRepository apiConfiguration; + + private VNWConfigurationResponse configurationResponse; + + @Resource + private JobQueryBuilder jobQueryBuilder; + + @Resource + private ElasticsearchTemplate elasticsearchTemplate; + + @Resource + private VietnamworksJobRepository vietnamworksJobRepository; + + private static final long VIETNAM_CURRENCY_SALARY_DETECTOR = 1000000L; + + private static final long VND_USD_RATE = 21000L; + + private static final int SORT_ORDER_ASC = 1; + + private static final int SORT_ORDER_DESC = -1; + + /** + * Get the configuration from Vietnamworks API such as job locations, categories, degree, etc + * + * @return The configuration from the API {@link com.techlooper.model.VNWConfigurationResponse} + */ + public VNWConfigurationResponse getConfiguration() { + return Optional.ofNullable(configurationResponse).orElseGet(() -> { + HttpEntity requestEntity = RestTemplateUtils.configureHttpRequestEntity( + MediaType.APPLICATION_JSON, apiConfiguration.getApiKeyName(), apiConfiguration.getApiKeyValue(), EMPTY); + ResponseEntity responseEntity = restTemplate.exchange( + apiConfiguration.getConfigurationUrl(), HttpMethod.GET, requestEntity, String.class); + final Optional configuration = Optional.ofNullable(responseEntity.getBody()); + + if (configuration.isPresent()) { + configurationResponse = JsonUtils.toPOJO(configuration.get(), VNWConfigurationResponse.class). + orElseGet(VNWConfigurationResponse::new); + } + return configurationResponse; + }); } - return VNWJobSearchResponse.getDefaultObject(); - } - - /** - * Merge the search result with configuration in order to get its meaningful name - * - * @param jobSearchResponse The job search response - * @param configuration The job configuration - */ - private void mergeSearchResultWithConfiguration(VNWJobSearchResponse jobSearchResponse, - VNWConfigurationResponse configuration) { - BiFunction idTranslator = (itemId, idType) -> - mergeConfigurationItem(configuration, itemId, idType); - - Stream responseDataItemStream = jobSearchResponse.getData().getJobs().stream(); - responseDataItemStream.forEach(responseDataItem -> { - responseDataItem.setLocation(idTranslator.apply(responseDataItem.getLocation(), JOB_LOCATION)); - responseDataItem.setLevel(idTranslator.apply(responseDataItem.getLevel(), JOB_LEVEL)); - - }); - } - - /** - * Merge the search result with configuration in order to get its meaningful name - * - * @param configuration The job configuration - * @param itemId List of item IDs should be merged - * @param idType The kind of id such as location, level or category - * @return The item name value after merging, separated by comma - */ - private String mergeConfigurationItem(VNWConfigurationResponse configuration, String itemId, String idType) { - final String COMMA = ","; - Function translateConfigurationFunc = (id) -> translateConfigurationId(id, idType, configuration); - - return Stream.of(itemId.split(COMMA)).distinct() - .map(translateConfigurationFunc).collect(Collectors.joining(COMMA)); - } - - /** - * Merge the search result with configuration in order to get its meaningful name - * - * @param id Unique ID value - * @param itemType The kind of id such as location, level or category - * @param configuration The job configuration - * @return The unique item name value after merging - */ - private String translateConfigurationId(String id, String itemType, VNWConfigurationResponse configuration) { - switch (itemType) { - case JOB_LOCATION: - Optional locationOptional = configuration.getData().getLocations().stream() - .filter(item -> item.getLocationId().equals(id)) - .findFirst(); - return locationOptional.isPresent() ? locationOptional.get().getEnglish() : EMPTY; - case JOB_LEVEL: - Optional degreeOptional = - configuration.getData().getDegrees().stream() - .filter(item -> item.getDegreeId().equals(id)) - .findFirst(); - return degreeOptional.isPresent() ? degreeOptional.get().getEnglish() : EMPTY; + + /** + * Get the job search result from Vietnamworks API which matches the criteria terms + * + * @param jobSearchRequest The job search request which contains the criteria terms and page number + * @return The job search result from the API {@link com.techlooper.model.VNWJobSearchResponse} + */ + public VNWJobSearchResponse searchJob(VNWJobSearchRequest jobSearchRequest) { + final String searchParameters = JsonUtils.toJSON(jobSearchRequest).orElse(EMPTY); + HttpEntity requestEntity = RestTemplateUtils.configureHttpRequestEntity( + MediaType.APPLICATION_JSON, apiConfiguration.getApiKeyName(), apiConfiguration.getApiKeyValue(), searchParameters); + ResponseEntity responseEntity = restTemplate.exchange( + apiConfiguration.getSearchUrl(), HttpMethod.POST, requestEntity, String.class); + + final Optional jobSearchResponseJson = Optional.ofNullable(responseEntity.getBody()); + + if (jobSearchResponseJson.isPresent()) { + final VNWJobSearchResponse actualResult = JsonUtils.toPOJO(jobSearchResponseJson.get(), VNWJobSearchResponse.class) + .orElse(VNWJobSearchResponse.getDefaultObject()); + + if (actualResult.hasData()) { + //mergeSearchResultWithConfiguration(actualResult, getConfiguration()); + return actualResult; + } + } + return VNWJobSearchResponse.getDefaultObject(); } - return EMPTY; - } - - public List getHigherSalaryJobs(SalaryReviewEntity salaryReviewEntity) { - NativeSearchQueryBuilder queryBuilder = jobQueryBuilder.getVietnamworksJobSearchQuery(); - - QueryBuilder jobTitleQueryBuilder = jobQueryBuilder.jobTitleQueryBuilder(salaryReviewEntity.getJobTitle()); - FilterBuilder expiredDateFilterBuilder = jobQueryBuilder.getRangeFilterBuilder("expiredDate", "now/d", null); - - queryBuilder.withQuery(filteredQuery(jobTitleQueryBuilder, boolFilter() - .must(expiredDateFilterBuilder) - .must(termFilter("isActive", 1)) - .must(termFilter("isApproved", 1)) - .must(jobQueryBuilder.getJobIndustriesFilterBuilder(salaryReviewEntity.getJobCategories())))); - List higherSalaryJobs = getJobSearchResult(queryBuilder); - return higherSalaryJobs.stream() - .filter(job -> getAverageSalary(job.getSalaryMin(), job.getSalaryMax()) > salaryReviewEntity.getNetSalary()) - .sorted((job1, job2) -> jobSalaryComparator(job1, job2, SORT_ORDER_DESC)) - .limit(3).collect(Collectors.toList()); - } - - private int jobSalaryComparator(JobEntity job1, JobEntity job2, int sortOrder) { - double avgSalaryJob1 = getAverageSalary(job1.getSalaryMin(), job1.getSalaryMax()); - double avgSalaryJob2 = getAverageSalary(job2.getSalaryMin(), job2.getSalaryMax()); - - if (avgSalaryJob1 > avgSalaryJob2) { - return 1 * sortOrder; + + /** + * Merge the search result with configuration in order to get its meaningful name + * + * @param jobSearchResponse The job search response + * @param configuration The job configuration + */ + private void mergeSearchResultWithConfiguration(VNWJobSearchResponse jobSearchResponse, + VNWConfigurationResponse configuration) { + BiFunction idTranslator = (itemId, idType) -> + mergeConfigurationItem(configuration, itemId, idType); + + Stream responseDataItemStream = jobSearchResponse.getData().getJobs().stream(); + responseDataItemStream.forEach(responseDataItem -> { + responseDataItem.setLocation(idTranslator.apply(responseDataItem.getLocation(), JOB_LOCATION)); + responseDataItem.setLevel(idTranslator.apply(responseDataItem.getLevel(), JOB_LEVEL)); + + }); } - else if (avgSalaryJob2 > avgSalaryJob1) { - return -1 * sortOrder; + + /** + * Merge the search result with configuration in order to get its meaningful name + * + * @param configuration The job configuration + * @param itemId List of item IDs should be merged + * @param idType The kind of id such as location, level or category + * @return The item name value after merging, separated by comma + */ + private String mergeConfigurationItem(VNWConfigurationResponse configuration, String itemId, String idType) { + final String COMMA = ","; + Function translateConfigurationFunc = (id) -> translateConfigurationId(id, idType, configuration); + + return Stream.of(itemId.split(COMMA)).distinct() + .map(translateConfigurationFunc).collect(Collectors.joining(COMMA)); } - return 0; - } - - public Double getAverageSalary(Long salaryMin, Long salaryMax) { - salaryMin = convertVND2ToUSD(salaryMin); - salaryMax = convertVND2ToUSD(salaryMax); - if (salaryMin == 0) { - return salaryMax * 0.75D; + + /** + * Merge the search result with configuration in order to get its meaningful name + * + * @param id Unique ID value + * @param itemType The kind of id such as location, level or category + * @param configuration The job configuration + * @return The unique item name value after merging + */ + private String translateConfigurationId(String id, String itemType, VNWConfigurationResponse configuration) { + switch (itemType) { + case JOB_LOCATION: + Optional locationOptional = configuration.getData().getLocations().stream() + .filter(item -> item.getLocationId().equals(id)) + .findFirst(); + return locationOptional.isPresent() ? locationOptional.get().getEnglish() : EMPTY; + case JOB_LEVEL: + Optional degreeOptional = + configuration.getData().getDegrees().stream() + .filter(item -> item.getDegreeId().equals(id)) + .findFirst(); + return degreeOptional.isPresent() ? degreeOptional.get().getEnglish() : EMPTY; + } + return EMPTY; } - else if (salaryMax == 0) { - return salaryMin * 1.25D; + + public List getHigherSalaryJobs(SalaryReviewEntity salaryReviewEntity) { + NativeSearchQueryBuilder queryBuilder = jobQueryBuilder.getVietnamworksJobSearchQuery(); + + QueryBuilder jobTitleQueryBuilder = jobQueryBuilder.jobTitleQueryBuilder(salaryReviewEntity.getJobTitle()); + FilterBuilder expiredDateFilterBuilder = jobQueryBuilder.getRangeFilterBuilder("expiredDate", "now/d", null); + + queryBuilder.withQuery(filteredQuery(jobTitleQueryBuilder, boolFilter() + .must(expiredDateFilterBuilder) + .must(termFilter("isActive", 1)) + .must(termFilter("isApproved", 1)) + .must(jobQueryBuilder.getJobIndustriesFilterBuilder(salaryReviewEntity.getJobCategories())))); + List higherSalaryJobs = getJobSearchResult(queryBuilder); + return higherSalaryJobs.stream() + .filter(job -> getAverageSalary(job.getSalaryMin(), job.getSalaryMax()) > salaryReviewEntity.getNetSalary()) + .sorted((job1, job2) -> jobSalaryComparator(job1, job2, SORT_ORDER_DESC)) + .limit(3).collect(Collectors.toList()); } - return (salaryMin + salaryMax) / 2D; - } - - public List getJobSearchResult(NativeSearchQueryBuilder queryBuilder) { - queryBuilder.withSearchType(SearchType.DEFAULT); - long totalJobs = elasticsearchTemplate.count(queryBuilder.build()); - long totalPage = totalJobs % 100 == 0 ? totalJobs / 100 : totalJobs / 100 + 1; - int pageIndex = 0; - List jobs = new ArrayList<>(); - while (pageIndex < totalPage) { - queryBuilder.withPageable(new PageRequest(pageIndex, 100)); - jobs.addAll(elasticsearchTemplate.queryForPage(queryBuilder.build(), JobEntity.class).getContent()); - pageIndex++; + + private int jobSalaryComparator(JobEntity job1, JobEntity job2, int sortOrder) { + double avgSalaryJob1 = getAverageSalary(job1.getSalaryMin(), job1.getSalaryMax()); + double avgSalaryJob2 = getAverageSalary(job2.getSalaryMin(), job2.getSalaryMax()); + + if (avgSalaryJob1 > avgSalaryJob2) { + return 1 * sortOrder; + } else if (avgSalaryJob2 > avgSalaryJob1) { + return -1 * sortOrder; + } + return 0; } - return jobs; - } - private Long convertVND2ToUSD(Long salary) { - return salary > VIETNAM_CURRENCY_SALARY_DETECTOR ? salary / VND_USD_RATE : salary; - } + public Double getAverageSalary(Long salaryMin, Long salaryMax) { + salaryMin = convertVND2ToUSD(salaryMin); + salaryMax = convertVND2ToUSD(salaryMax); + if (salaryMin == 0) { + return salaryMax * 0.75D; + } else if (salaryMax == 0) { + return salaryMin * 1.25D; + } + return (salaryMin + salaryMax) / 2D; + } + + @Override + public JobEntity findJobById(String id) { + return vietnamworksJobRepository.findOne(id); + } + + @Override + public JobEntity updateJob(JobEntity jobEntity) { + return vietnamworksJobRepository.save(jobEntity); + } + + public List getJobSearchResult(NativeSearchQueryBuilder queryBuilder) { + queryBuilder.withSearchType(SearchType.DEFAULT); + long totalJobs = elasticsearchTemplate.count(queryBuilder.build()); + long totalPage = totalJobs % 100 == 0 ? totalJobs / 100 : totalJobs / 100 + 1; + int pageIndex = 0; + List jobs = new ArrayList<>(); + while (pageIndex < totalPage) { + queryBuilder.withPageable(new PageRequest(pageIndex, 100)); + jobs.addAll(elasticsearchTemplate.queryForPage(queryBuilder.build(), JobEntity.class).getContent()); + pageIndex++; + } + return jobs; + } + + private Long convertVND2ToUSD(Long salary) { + return salary > VIETNAM_CURRENCY_SALARY_DETECTOR ? salary / VND_USD_RATE : salary; + } } \ No newline at end of file diff --git a/src/main/java/com/techlooper/service/impl/WebinarServiceImpl.java b/src/main/java/com/techlooper/service/impl/WebinarServiceImpl.java index 595a4418c..2c5891b80 100644 --- a/src/main/java/com/techlooper/service/impl/WebinarServiceImpl.java +++ b/src/main/java/com/techlooper/service/impl/WebinarServiceImpl.java @@ -18,7 +18,6 @@ import freemarker.template.Template; import freemarker.template.TemplateException; import org.dozer.Mapper; -import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; @@ -41,7 +40,6 @@ import java.io.IOException; import java.io.StringWriter; import java.util.*; -import java.util.stream.Collectors; /** * Created by phuonghqh on 8/18/15. @@ -142,11 +140,8 @@ public Collection findAllWebinars() { public List listUpcomingWebinar() { List upcomingWebinars = new ArrayList<>(); NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder().withTypes("webinar"); - - searchQueryBuilder.withQuery(QueryBuilders.rangeQuery("startDate") - .from(org.joda.time.DateTime.now().toString("dd/MM/yyyy hh:mm a"))); - - searchQueryBuilder.withSort(SortBuilders.fieldSort("startDate").order(SortOrder.DESC)); + searchQueryBuilder.withPageable(new PageRequest(0, 4)).withQuery(QueryBuilders.rangeQuery("startDate").from("now")) + .withSort(SortBuilders.fieldSort("startDate").order(SortOrder.ASC)); List webinarEntities = webinarRepository.search(searchQueryBuilder.build()).getContent(); if (!webinarEntities.isEmpty()) { @@ -156,8 +151,8 @@ public List listUpcomingWebinar() { } } - Collections.reverse(upcomingWebinars); - return upcomingWebinars.stream().limit(4).collect(Collectors.toList()); +// Collections.reverse(upcomingWebinars); + return upcomingWebinars;//.stream().limit(4).collect(Collectors.toList()); } public WebinarInfoDto findWebinarById(Long id) { diff --git a/src/main/resources/template/salaryReviewReport.en.ftl b/src/main/resources/template/salaryReviewReport.en.ftl index 611359b50..668476db5 100644 --- a/src/main/resources/template/salaryReviewReport.en.ftl +++ b/src/main/resources/template/salaryReviewReport.en.ftl @@ -430,7 +430,7 @@
- Update My Report + Update My Report
diff --git a/src/main/resources/template/salaryReviewReport.vi.ftl b/src/main/resources/template/salaryReviewReport.vi.ftl index 46048d697..bef006234 100644 --- a/src/main/resources/template/salaryReviewReport.vi.ftl +++ b/src/main/resources/template/salaryReviewReport.vi.ftl @@ -430,7 +430,7 @@
- Cập Nhật Báo Cáo Lương + Cập Nhật Báo Cáo Lương
diff --git a/src/main/webapp/assets/modules/common/timestampFilter.js b/src/main/webapp/assets/modules/common/timestampFilter.js index 9a43e2772..dcfecb410 100644 --- a/src/main/webapp/assets/modules/common/timestampFilter.js +++ b/src/main/webapp/assets/modules/common/timestampFilter.js @@ -12,6 +12,9 @@ techlooper.filter("timestamp", function (jsonValue) { case 'shortDate': return moment(input, jsonValue.dateFormat).format('ddd, DD MMMM'); + + case 'longDate': + return moment(input, jsonValue.dateFormat).format('ddd, DD MMMM YYYY'); } var duration = Math.abs(moment(date).diff(moment(), "days")); diff --git a/src/main/webapp/assets/modules/common/utils.fac.js b/src/main/webapp/assets/modules/common/utils.fac.js index 0bf6e07ee..6bf8ea820 100644 --- a/src/main/webapp/assets/modules/common/utils.fac.js +++ b/src/main/webapp/assets/modules/common/utils.fac.js @@ -111,6 +111,7 @@ techlooper.factory("utils", function (jsonValue, $location, $rootScope, localSto str = str.replace(/đ/g, "d"); str = str.replace(/!|@|\$|%|\^|\*|\(|\)|\+|\=|\<|\>|\?|\/|,|\.|\:|\'| |\"|\&|\#|\[|\]|~/g, "-"); str = str.replace(/-+-/g, "-"); //change "--" to "-" + str = str.replace(/_/g, "-"); //change "--" to "-" str = str.replace(/^\-+|\-+$/g, "");//trim "-" return str; }, diff --git a/src/main/webapp/assets/modules/common/vnwConfigService.js b/src/main/webapp/assets/modules/common/vnwConfigService.js index c6f342346..ec564d320 100644 --- a/src/main/webapp/assets/modules/common/vnwConfigService.js +++ b/src/main/webapp/assets/modules/common/vnwConfigService.js @@ -387,11 +387,6 @@ techlooper.factory("vnwConfigService", function (jsonValue, $translate, $rootSco "lang_vn": "Khác", "lang_en": "Other" }, - { - "location_id": "70", - "lang_vn": "Quốc tế", - "lang_en": "International" - }, { "location_id": "72", "lang_vn": "Hậu Giang", @@ -401,14 +396,14 @@ techlooper.factory("vnwConfigService", function (jsonValue, $translate, $rootSco var educationLevel = [ { - "education_id": "1", - "lang_vn": "Không nêu", - "lang_en": "None" + "education_id": "12", + "lang_vn": "Tiến sĩ", + "lang_en": "Doctorate" }, { - "education_id": "2", - "lang_vn": "Trung học", - "lang_en": "High school" + "education_id": "9", + "lang_vn": "Thạc sĩ", + "lang_en": "Masters" }, { "education_id": "3", @@ -421,37 +416,17 @@ techlooper.factory("vnwConfigService", function (jsonValue, $translate, $rootSco "lang_en": "Diploma" }, { - "education_id": "12", - "lang_vn": "Cao đẳng", - "lang_en": "College" - }, - { - "education_id": "14", - "lang_vn": "Kỹ Sư", - "lang_en": "Bachelor of Engineering" - }, - { - "education_id": "13", - "lang_vn": "Sau đại học", - "lang_en": "Post-graduate" - }, - { - "education_id": "5", - "lang_vn": "Thạc Sĩ", - "lang_en": "Masters" - }, - { - "education_id": "6", - "lang_vn": "Tiến sĩ", - "lang_en": "Doctorate" + "education_id": "2", + "lang_vn": "Trung học", + "lang_en": "High school" }, { - "education_id": "7", - "lang_vn": "Thạc sĩ Quản trị Kinh doanh", - "lang_en": "MBA" + "education_id": "1", + "lang_vn": "Tất cả trình độ", + "lang_en": "None" }, { - "education_id": "11", + "education_id": "15", "lang_vn": "Khác", "lang_en": "Others" } @@ -513,7 +488,7 @@ techlooper.factory("vnwConfigService", function (jsonValue, $translate, $rootSco var experiences = [ { "experience_id": "1", - "lang_vn": "Lực lượng lao động mới", + "lang_vn": "Mới tốt nghiệp", "lang_en": "New to workforce" }, { diff --git a/src/main/webapp/assets/modules/event-details/event-details.html b/src/main/webapp/assets/modules/event-details/event-details.html index 432c43ce0..b5fa8b427 100644 --- a/src/main/webapp/assets/modules/event-details/event-details.html +++ b/src/main/webapp/assets/modules/event-details/event-details.html @@ -13,9 +13,9 @@

{{webinar.name}}

- {{webinar.startDate | timestamp: 'shortDate'}} - {{webinar.startDate | timestamp: 'hour'}} + {{webinar.startDate | timestamp: 'longDate'}} - {{webinar.startDate | timestamp: 'hour'}} - {{webinar.endDate | timestamp: 'shortDate'}} - {{webinar.endDate | timestamp: 'hour'}} + {{webinar.endDate | timestamp: 'longDate'}} - {{webinar.endDate | timestamp: 'hour'}}
Google Hangouts diff --git a/src/main/webapp/assets/modules/home-page/home.html b/src/main/webapp/assets/modules/home-page/home.html index 0927d51d9..b9f5f6405 100644 --- a/src/main/webapp/assets/modules/home-page/home.html +++ b/src/main/webapp/assets/modules/home-page/home.html @@ -211,7 +211,7 @@

-

{{event.startDate | timestamp: 'shortDate'}}

+

{{event.startDate | timestamp: 'longDate'}}

diff --git a/src/main/webapp/assets/modules/job-listing/job-listing.html b/src/main/webapp/assets/modules/job-listing/job-listing.html index 464dae4ce..8e68cea8d 100644 --- a/src/main/webapp/assets/modules/job-listing/job-listing.html +++ b/src/main/webapp/assets/modules/job-listing/job-listing.html @@ -68,7 +68,7 @@

{{job.title}}

  • {{job.postedOn}}
  • -
  • {{job.location}}
  • +
  • {{job.locationText}}

: {{'negotiable' | translate}} @@ -111,7 +111,7 @@

{{job.

{{job.title}}

  • {{job.postedOn}}
  • -
  • {{job.location}}
  • +
  • {{job.locationText}}

: {{'negotiable' | translate}} diff --git a/src/main/webapp/assets/modules/job-listing/jobListingController.js b/src/main/webapp/assets/modules/job-listing/jobListingController.js index 62546d34e..98c02f5ac 100644 --- a/src/main/webapp/assets/modules/job-listing/jobListingController.js +++ b/src/main/webapp/assets/modules/job-listing/jobListingController.js @@ -21,6 +21,24 @@ techlooper.controller("jobListingController", function (apiService, $scope, vnwC $scope.totalJob = response.totalJob; $scope.page = response.page; $scope.jobs = response.jobs; + $.each($scope.jobs, function (i, job) { + var locations = job.location.split(","); + $.each(locations, function(j, id){ + if($.isNumeric(id)){ + if(j == 0){ + job.locationText = vnwConfigService.getLocationText(id); + }else{ + job.locationText = job.locationText + ', ' + vnwConfigService.getLocationText(id); + } + }else{ + if(j == 0){ + job.locationText = id; + }else{ + job.locationText = job.locationText + ', ' + job.locationText+ id; + } + } + }); + }); }).finally(function () { utils.sendNotification(jsonValue.notifications.loaded); }); @@ -40,6 +58,25 @@ techlooper.controller("jobListingController", function (apiService, $scope, vnwC $scope.totalJob = response.totalJob; $scope.page = response.page; $scope.jobs = response.jobs; + $.each($scope.jobs, function (i, job) { + var locations = job.location.split(","); + $.each(locations, function(j, id){ + if($.isNumeric(id)){ + if(j == 0){ + job.locationText = vnwConfigService.getLocationText(id); + }else{ + job.locationText = job.locationText + ', ' + vnwConfigService.getLocationText(id); + } + }else{ + if(j == 0){ + job.locationText = id; + }else{ + job.locationText = job.locationText + ', ' + job.locationText+ id; + } + } + }); + }); + console.log($scope.jobs); }); $scope.searchJob = {keyword : keyword, locationId : locationId, location : location}; diff --git a/src/main/webapp/assets/modules/navigation/employer-header.tem.html b/src/main/webapp/assets/modules/navigation/employer-header.tem.html index 84cae168b..5c92c6367 100644 --- a/src/main/webapp/assets/modules/navigation/employer-header.tem.html +++ b/src/main/webapp/assets/modules/navigation/employer-header.tem.html @@ -24,7 +24,7 @@


     <div class=

- -
@@ -24,7 +22,7 @@


         <div class=
  • - {{$root.userInfo.firstName | limitTo: 10}} + {{($root.userInfo.name | limitTo:10) || ($root.userInfo.username | textTruncate: 'email')}}
  • diff --git a/src/main/webapp/assets/modules/salary-report/sr-promotion-company.js b/src/main/webapp/assets/modules/salary-report/sr-promotion-company.js index f7127d22f..65340a2ba 100644 --- a/src/main/webapp/assets/modules/salary-report/sr-promotion-company.js +++ b/src/main/webapp/assets/modules/salary-report/sr-promotion-company.js @@ -10,6 +10,9 @@ techlooper.directive("srPromotionCompany", function ($http, validatorService, vn } scope.sendCitibankPromotion = function () { + if(scope.promotion.agree == undefined){ + return; + } var error = validatorService.validate($(".partner-company-form").find('input')); scope.error = error; if (!$.isEmptyObject(error)) { diff --git a/src/main/webapp/assets/sass/z-responsive-candidate.sass b/src/main/webapp/assets/sass/z-responsive-candidate.sass index d821766ed..cd0a9f26c 100644 --- a/src/main/webapp/assets/sass/z-responsive-candidate.sass +++ b/src/main/webapp/assets/sass/z-responsive-candidate.sass @@ -1428,10 +1428,13 @@ margin-top: 20px .alert-danger margin-top: 20px + .it-job-corner-content-form + display: block .latest-jobs-block width: 100% clear: both margin-bottom: 20px + display: block ul padding: 5px !important li:last-child diff --git a/src/test/es-query/job_alert_search_query.json b/src/test/es-query/job_alert_search_query.json index e7112f982..3b2affc50 100644 --- a/src/test/es-query/job_alert_search_query.json +++ b/src/test/es-query/job_alert_search_query.json @@ -4,16 +4,6 @@ GET techlooper/job/_search "filtered": { "query": { "bool": { - "must": [ - { - "match": { - "location": { - "query" : "Ha Noi", - "operator": "and" - } - } - } - ], "must": [ { "multi_match": { @@ -28,10 +18,28 @@ GET techlooper/job/_search } }, "filter": { - "range": { - "createdDateTime": { - "from": "now-30d/d" - } + "bool": { + "must": [ + { + "range": { + "createdDateTime": { + "from": "now-30d/d" + } + } + }, + { + "term": { + "topPriority": true + } + } + ], + "must_not": [ + { + "term": { + "isActive": 0 + } + } + ] } } } @@ -53,4 +61,4 @@ GET techlooper/job/_search } } ] -} +} \ No newline at end of file