Giter Club home page Giter Club logo

dev-job-analytics's Introduction

Jobservatory: A platform for the intersection between Technologies and Jobs

A platform to analyze technologies trend over time, by country, and by importance on StackOverFlow.

Backend Build Backend Lint Frontend Build Frontend Lint Scraper Build Scraper Lint Code style: black

Jobservatory Architecture

Fig. 1: Jobservatory Architecture

Scrapers

Under the scraper folder, there is all the code related to data pipelines which have the task of ingesting data into our database. Currently we are using MongoDB as our DB engine, because schema evolution flexibility and the fast iterations that are possible under this model. We are currently scraping data from two sources: Linkedin and StackOverflow.

Jobservatory Express

Our backend is an express server (Nodejs), from which we implement endpoints for several queries. The related code is under jobservatory_express directory. We also cache requests under the assumption that data will not vary faster than 10 minutes, so we have a redis cache to improve latency. Our performance tests showed that the reduction in latency was from 400 ms to 3 ms on average.

Jobservatory React

As the name suggests, our frontend is built on React framework. Code is under jobservatory_react. For manage the states inside the react app we use Redux, for UI layouts in general Chakra-ui, and for charts we use Nivo. Tests are under react-testing-library reaching 70%.

jobservatoryShortSize

dev-job-analytics's People

Contributors

diekrul avatar dpalmasan avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

diekrul

dev-job-analytics's Issues

Script para crear respaldo diario de la base de datos

  • Ayer 2021-09-01 tuvimos un incidente intentando eliminar algunos documentos inválidos de la DB
  • Por error se perdieron todos los datos de la colección scrapeada desde linkedin

Debemos crear un job que respalde la DB de forma diaria.

Actualizar LoggedIn Scraper para aceptar un driver de Selenium con opciones

En el deployment a heroku, me di cuenta que es mejor tener la posibilidad de agregar un driver de selenium con opciones, por ejemplo:

class LoggedLinkedinScrapper(LinkedinScraper):
    """Implement a LinkedinScraper using identity."""

    def __init__(
        self,
        email: str,
        password: str,
        driver: Optional[webdriver.chrome.webdriver.WebDriver] = None,
    ):
        """Start ``selenium`` driver to scrap from the web.

        :param email: Email to login into Linkedin
        :type email: str
        :param password: Password
        :type password: str
        """
        self.driver = driver
        if self.driver is None:
            self.driver = webdriver.Chrome(ChromeDriverManager().install())
        self.email = email
        self.password = password
        self.logged = False

De esta forma se puede hacer algo como:

chrome_options = webdriver.ChromeOptions()
chrome_options.binary_location = os.environ.get("GOOGLE_CHROME_BIN")
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--no-sandbox")
driver = webdriver.Chrome(
    executable_path=os.environ.get("CHROMEDRIVER_PATH"),
    chrome_options=chrome_options,
)
sc = LoggedLinkedinScrapper(EMAIL, PASSWORD, driver)

Crear tests para backend

Por completitud, deberíamos tener tests para todo el código. Este ticket es para implementar tests en el backend. Probablemente utilizaremos mocha.

Agregar cache para requests

Podríamos considerar redis, ya que sabemos que los datos se actualizan diariamente, no tiene sentido hacer llamadas a la DB cada vez que pedimos datos si ya sabemos que estará en cache. Esto involucrará actualizar los endpoint del backend.

Hacer refactor a uso de `moment`

Tenemos varias funciones del backend que usan moment, lo que convierte a las funciones en no idempotentes. Idealmente, deberíamos hacer la fecha una entrada a estas funciones.

Investigar como automatizar scripts de ingesta de datos

El script backfill_so_data es fácil de automatizar porque hace un simple request sin autorización. El script problemático es el scraper de linkedin, ya que requiere logearse desde la máquina. Idea, apuntar display a una máquina local y logearse.

chrome_options = webdriver.ChromeOptions()
chrome_options.binary_location = os.environ.get("GOOGLE_CHROME_BIN")
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--no-sandbox")
driver = webdriver.Chrome(
    executable_path=os.environ.get("CHROMEDRIVER_PATH"),
    chrome_options=chrome_options,
)

Agregar URI del backend a configuración

Actualmente, la URI del backend está hardcodeada en el frontend. Para el release, idealmente deberíamos tener esta URI en el archivo de configuración, ya que es una parte de la configuración, por ejemplo:

https://github.com/dpalmasan/dev-job-analytics/blob/main/jobservatory_react/src/components/detail/Detail.tsx#L37

Lo ideal sería quitar la URI harcodeada y hacerla parte de una configuración. Lo único fijo en la URI sería la API (el host y el puerto deberían ser parte de la configuración por ahora)

Actualizar README general y componentes

Para el README.md general estaría bueno poner gifs, o una vista más dinámica de la aplicación. Lo mismo para los componentes:

  • jobservatory_express
  • jobservatory_react
  • scrapper

Agregar más documentación de uso, y ejemplos.

Crear scrapper de datos de linkedin

El scrapper debe estar conectado con la Base de Datos y debe ejecutarse en un período determinado para actualizar la base de datos.

El link funciona de esta forma
https://www.linkedin.com/jobs/search/?geoId=92000000&keywords=React.js&location=Todo%20el%20mundo

Ahi se saca el dato de todo el mundo de la tecnologia React.js (se puede hacer con navegador incognito no necesita sesion)

Screen Shot 2021-08-03 at 12 41 08 PM

La forma en la que creo que serviria recibir desde el front, sigue este patron.

https://github.com/dpalmasan/dev-job-analytics/blob/main/jobservatory_react/src/fakeFinalData.js

Aumentar coverage de backend a 85%

Actualmente el coverage está bajo el umbral que se decidió por acuerdo. Se deben crear tests de manera de aumentar el coverage al umbral esperado. También se debe actualizar el package.json:

"scripts": {
    "start": "node index",
    "server": "nodemon index",
    "test": "nyc --check-coverage --lines 75 mocha tests",
    "client": "npm start --prefix ./../jobservatory",
    "dev": "concurrently \"npm run server\" \"npm run client\"",
    "linter": "eslint . --ext .js,.jsx,.ts,.tsx"
  },

Se debe actualizar --lines 75 a --lines 85.

[MVP] Modelo de datos y documentación

Para el MVP tenemos que tener claro el modelo de datos, ya que el scrapper alimentará la base de datos y por lo tanto hay que tener los schemas definidos, además de agregar a la documentación del proyecto (en gdrive) el modelo de datos a utilizar.

Ya existe un mock de datos del cual el frontend está basado:

https://github.com/dpalmasan/dev-job-analytics/blob/main/jobservatory_react/src/fakeFinalData.js

Por lo que hay que agregar la lógica de ingesta de datos a la BBDD basado en este prototipo y completar la documentación.

Agregar chequeos de coverage (frontend, backend, scraper)

Idealmente, queremos tener la mayoría del código cubierto por casos de prueba. Inicialmente, deberíamos apuntar a un coverage de un 85% (ideal 100%). Agregar a los linters chequeos de coverage:

  • scraper (pytest-cov)
  • backend (mocha)
  • frontend (tengo entendido que aún no tenemos tests para esto)

Agregar pipeline CI para frontend (test + coverage)

El scraper y el backend tienen sus suite de pruebas, y pipeline CI configurado. Sólo queda setear CI en el frontend. Ya que @Diekrul quieres aprender sobre esto, te recomiendo tomar este issue.

Requerimientos

  • Suite de pruebas creadas para el frontend
  • Script creado para ejecutar pruebas (ej: yarn test)
  • Pipeline CI configurado (si quieres probar en local puedes usar esta herramienta https://github.com/nektos/act; Ojo que necesita tener Docker instalado).
  • Agregar chequeos de coverage a las pruebas (y definir un umbral, por ejemplo 85%)

Actualizar script get_data para usar uri personalizada en la conexión con MongoDB

Actualmente tenemos esto:

def main():
    """Entry point."""
    connect("jobservatory-manual")
    with open("jobserver-config.yaml", "r") as fp:
        creds = yaml.safe_load(fp)

    sc = LoggedLinkedinScrapper(creds["email"], creds["password"])
    import_data(
        sc,
        [
            "React.js",
            "Angular.js",
            "Ruby on Rails",
            "Java",
            "Python",
            "Data Engineer",
        ],
        0,
    )

Pero sería mejor actualizar el jobserver-config.yaml con la opción de tener una URI personalizada y conectar a cualquier DB (local o remota)

Implementar Scrapper con inicio de sesión

Para un scrapping adhoc, se puede utilizar Selenium para extraer el conteo de puestos teniendo sesión iniciada, por ejemplo el siguiente snippet:

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager


  
VERIFY_LOGIN_ID = "global-nav-search"
REMEMBER_PROMPT = 'remember-me-prompt__form-primary'

driver = webdriver.Chrome(ChromeDriverManager().install())
email = "EMAIL"
password = "PASSWORD"

driver.get("https://www.linkedin.com/login")
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "username")))
email_elem = driver.find_element_by_id("username")
email_elem.send_keys(email)

password_elem = driver.find_element_by_id("password")
password_elem.send_keys(password)
password_elem.submit()

driver.get("https://www.linkedin.com/jobs/search/?geoId=92000000&keywords=React.js&location=Todo%20el%20mundo")
element = driver.find_element_by_tag_name("small")
print(element.text)

Se puede implementar una clase nueva, algo como LoggedInLinkedinScrapper para extraer estos resultados.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.