├── .gitignore ├── README.md ├── jobsearch ├── __init__.py ├── dataaccess.py ├── jobs.py └── remoteok.py ├── main.py ├── poetry.lock └── pyproject.toml /.gitignore: -------------------------------------------------------------------------------- 1 | instance/ 2 | __pycache__/ 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AI-based Remote Job Search 2 | 3 | **Find your next remote job using a little bit of Python and machine learning!** 4 | 5 | This tool is based on RemoteOK API and makes it possible to get job recommendations based on your preferences. 6 | 7 | This is a study application, you will need to improve it for it to really shine! 8 | 9 | Read the article: [Finding Remote Work with Python and AI](https://stribny.name/blog/python-job-search/) 10 | 11 | ## Installation 12 | 13 | Use Poetry to install and run commands: 14 | 15 | ``` 16 | poetry install 17 | poetry shell 18 | ``` 19 | 20 | ## Usage 21 | 22 | The program is a CLI application that exposes `fetch-new`, `label`, `train` and `recommend` commands: 23 | 24 | ``` 25 | # fetch new job posts from RemoteOk 26 | python main.py fetch-new 27 | 28 | # start data labelling 29 | python main.py label 30 | 31 | # train classifier on labeled data 32 | python main.py train 33 | 34 | # display recommended jobs 35 | python main.py recommend 36 | ``` 37 | 38 | ## Licence 39 | 40 | Author: [Petr Stříbný](http://stribny.name) 41 | 42 | License: The MIT License (MIT) 43 | -------------------------------------------------------------------------------- /jobsearch/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.0' 2 | -------------------------------------------------------------------------------- /jobsearch/dataaccess.py: -------------------------------------------------------------------------------- 1 | from contextlib import contextmanager 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import sessionmaker 5 | from sqlalchemy.orm import Session 6 | from sqlalchemy import ( 7 | Column, 8 | Integer, 9 | String, 10 | ) 11 | 12 | SQLALCHEMY_DATABASE_URL = "sqlite:///./instance/jobposts.db" 13 | engine = create_engine( 14 | SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} 15 | ) 16 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 17 | Base = declarative_base() 18 | 19 | 20 | @contextmanager 21 | def get_session() -> Session: 22 | session = SessionLocal() 23 | yield session 24 | session.close() 25 | 26 | 27 | class JobPost(Base): 28 | __tablename__ = "job_posts" 29 | 30 | id = Column(Integer, primary_key=True, index=True) 31 | url = Column(String, nullable=False) 32 | company = Column(String, nullable=False) 33 | position = Column(String, nullable=False) 34 | description = Column(String, nullable=False) 35 | location = Column(String, nullable=False) 36 | tags = Column(String, nullable=False) 37 | label = Column(Integer) 38 | 39 | @property 40 | def text(self): 41 | return "\n".join([ 42 | self.company, 43 | self.position, 44 | self.description, 45 | self.tags, 46 | self.location 47 | ]) 48 | 49 | 50 | class JobPostLabel: 51 | NOT_INTERESTED = 0 52 | INTERESTED = 1 53 | 54 | 55 | Base.metadata.create_all(bind=engine) 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /jobsearch/jobs.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from sqlalchemy.orm import Session 3 | from jobsearch.dataaccess import JobPost 4 | 5 | 6 | def save(session: Session, posts: list[JobPost]) -> None: 7 | session.bulk_save_objects(posts) 8 | session.commit() 9 | 10 | 11 | def update(session: Session, post: JobPost) -> None: 12 | session.add(post) 13 | session.commit() 14 | 15 | 16 | def get_last(session: Session) -> Optional[JobPost]: 17 | return session.query(JobPost).order_by(JobPost.id.desc()).first() 18 | 19 | 20 | def get_labeled(session: Session) -> list[JobPost]: 21 | return session.query(JobPost).filter(JobPost.label != None).all() 22 | 23 | 24 | def get_not_labeled(session: Session) -> list[JobPost]: 25 | return session.query(JobPost).filter(JobPost.label == None).all() 26 | 27 | 28 | def get_next_for_labelling(session: Session) -> Optional[JobPost]: 29 | return session.query(JobPost).filter(JobPost.label == None).first() 30 | -------------------------------------------------------------------------------- /jobsearch/remoteok.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from datetime import datetime 3 | import httpx 4 | 5 | 6 | # Change to automatically filter jobs by tags 7 | # https://remoteok.io/api?tags=php,database,xpath 8 | REMOTEOK_API_URL = "https://remoteok.io/api" 9 | 10 | 11 | @dataclass(frozen=True) 12 | class RemoteOkJobPost: 13 | id: int 14 | url: str 15 | company: str 16 | position: str 17 | description: str 18 | location: str 19 | tags: list[str] 20 | date: datetime 21 | 22 | @staticmethod 23 | def from_json(json): 24 | return RemoteOkJobPost( 25 | id=int(json['id']), 26 | url=json['url'], 27 | company=json['company'], 28 | position=json['position'], 29 | description=json['description'], 30 | location=json['location'], 31 | tags=json['tags'], 32 | date=datetime.fromisoformat(json['date']) 33 | ) 34 | 35 | 36 | def fetch_jobs() -> list[RemoteOkJobPost]: 37 | job_list = httpx.get(REMOTEOK_API_URL).json()[1:] 38 | return [RemoteOkJobPost.from_json(job_post) for job_post in job_list] -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import random 2 | from pathlib import Path 3 | import typer 4 | from rich.console import Console 5 | from rich.markdown import Markdown 6 | from rich.table import Table 7 | from sklearn.pipeline import Pipeline 8 | from sklearn.feature_extraction.text import CountVectorizer 9 | from sklearn.linear_model import SGDClassifier 10 | from sklearn.feature_extraction.text import TfidfTransformer 11 | from sklearn.model_selection import train_test_split 12 | from sklearn import metrics 13 | from joblib import dump, load 14 | from jobsearch.dataaccess import JobPost, JobPostLabel, get_session 15 | from jobsearch import jobs 16 | from jobsearch import remoteok 17 | 18 | 19 | app = typer.Typer() 20 | console = Console() 21 | model_path = Path("./instance/model.joblib") 22 | 23 | 24 | def transform_jp(jp: remoteok.RemoteOkJobPost) -> JobPost: 25 | return JobPost( 26 | id=jp.id, 27 | url=jp.url, 28 | company=jp.company, 29 | position=jp.position, 30 | description=jp.description, 31 | location=jp.location, 32 | tags=",".join(jp.tags) 33 | ) 34 | 35 | 36 | @app.command() 37 | def fetch_new(): 38 | console.print("[yellow]Fetching jobs...[/yellow]!") 39 | job_list = remoteok.fetch_jobs() 40 | job_list = [transform_jp(job_post) for job_post in job_list] 41 | with get_session() as session: 42 | last_job = jobs.get_last(session) 43 | if last_job is not None: 44 | job_list = [job_post for job_post in job_list if job_post.id > last_job.id] 45 | jobs.save(session, job_list) 46 | console.print("[green]Done![/green]") 47 | 48 | 49 | @app.command() 50 | def label(): 51 | while True: 52 | with get_session() as session: 53 | jp = jobs.get_next_for_labelling(session) 54 | if not jp: 55 | console.print("[red]No job posts available for labelling[/red]") 56 | break 57 | console.clear() 58 | console.print(f"[yellow]{jp.position}[/yellow]\n") 59 | console.print(f"[blue]{jp.company}[/blue]\n") 60 | console.print(f"[brown]{jp.tags}[/brown]\n") 61 | console.print(f"Location: {jp.location}\n") 62 | console.print(Markdown(f"{jp.description}")) 63 | result = typer.prompt("\n\nIs the job post relevant? [y yes/n no/q quit]", "n") 64 | if result in ["n", "no"]: 65 | jp.label = JobPostLabel.NOT_INTERESTED 66 | jobs.update(session, jp) 67 | if result in ["y", "yes"]: 68 | jp.label = JobPostLabel.INTERESTED 69 | jobs.update(session, jp) 70 | if result in ["q", "quit"]: 71 | console.clear() 72 | break 73 | 74 | 75 | @app.command() 76 | def train(): 77 | console.clear() 78 | console.print("[yellow]Training...[/yellow]!") 79 | job_clf = Pipeline([ 80 | ('vect', CountVectorizer()), 81 | ('tfidf', TfidfTransformer()), 82 | ('clf', SGDClassifier()), 83 | ]) 84 | with get_session() as session: 85 | labeled = jobs.get_labeled(session) 86 | if len(labeled) == 0: 87 | console.print("[red]No job posts available for labelling[/red]") 88 | return 89 | x = [jp.text for jp in labeled] 90 | y = [jp.label for jp in labeled] 91 | x_train, x_test, y_train, y_test = train_test_split( 92 | x, y, test_size = 0.2, random_state = random.randint(1, 1000) 93 | ) 94 | job_clf.fit(x_train, y_train) 95 | predicted = job_clf.predict(x_test) 96 | print(metrics.classification_report( 97 | y_test, predicted, target_names=["not interested", "interested"], labels=[0, 1]) 98 | ) 99 | dump(job_clf, model_path) 100 | 101 | 102 | @app.command() 103 | def recommend(): 104 | if not model_path.is_file(): 105 | console.print("[red]Model is not trained yet[/red]") 106 | return 107 | with get_session() as session: 108 | job_list = jobs.get_not_labeled(session) 109 | table = Table(show_header=True, header_style="bold magenta") 110 | table.add_column("Position", style="dim") 111 | table.add_column("Url") 112 | job_clf = load(model_path) 113 | predicted = job_clf.predict([jp.text for jp in job_list]) 114 | console.print("[yellow]Recommended jobs:[/yellow]") 115 | for i, jp in enumerate(job_list): 116 | if predicted[i] == JobPostLabel.INTERESTED: 117 | table.add_row(jp.position, jp.url) 118 | console.print(table) 119 | 120 | 121 | if __name__ == "__main__": 122 | app() -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "atomicwrites" 3 | version = "1.4.0" 4 | description = "Atomic file writes." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 8 | 9 | [[package]] 10 | name = "attrs" 11 | version = "20.3.0" 12 | description = "Classes Without Boilerplate" 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 16 | 17 | [package.extras] 18 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] 19 | docs = ["furo", "sphinx", "zope.interface"] 20 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] 21 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] 22 | 23 | [[package]] 24 | name = "certifi" 25 | version = "2020.12.5" 26 | description = "Python package for providing Mozilla's CA Bundle." 27 | category = "main" 28 | optional = false 29 | python-versions = "*" 30 | 31 | [[package]] 32 | name = "click" 33 | version = "7.1.2" 34 | description = "Composable command line interface toolkit" 35 | category = "main" 36 | optional = false 37 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 38 | 39 | [[package]] 40 | name = "colorama" 41 | version = "0.4.4" 42 | description = "Cross-platform colored terminal text." 43 | category = "main" 44 | optional = false 45 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 46 | 47 | [[package]] 48 | name = "commonmark" 49 | version = "0.9.1" 50 | description = "Python parser for the CommonMark Markdown spec" 51 | category = "main" 52 | optional = false 53 | python-versions = "*" 54 | 55 | [package.extras] 56 | test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] 57 | 58 | [[package]] 59 | name = "h11" 60 | version = "0.12.0" 61 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 62 | category = "main" 63 | optional = false 64 | python-versions = ">=3.6" 65 | 66 | [[package]] 67 | name = "httpcore" 68 | version = "0.12.2" 69 | description = "A minimal low-level HTTP client." 70 | category = "main" 71 | optional = false 72 | python-versions = ">=3.6" 73 | 74 | [package.dependencies] 75 | h11 = "<1.0.0" 76 | sniffio = ">=1.0.0,<2.0.0" 77 | 78 | [package.extras] 79 | http2 = ["h2 (>=3,<5)"] 80 | 81 | [[package]] 82 | name = "httpx" 83 | version = "0.16.1" 84 | description = "The next generation HTTP client." 85 | category = "main" 86 | optional = false 87 | python-versions = ">=3.6" 88 | 89 | [package.dependencies] 90 | certifi = "*" 91 | httpcore = ">=0.12.0,<0.13.0" 92 | rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} 93 | sniffio = "*" 94 | 95 | [package.extras] 96 | brotli = ["brotlipy (>=0.7.0,<0.8.0)"] 97 | http2 = ["h2 (>=3.0.0,<4.0.0)"] 98 | 99 | [[package]] 100 | name = "idna" 101 | version = "3.1" 102 | description = "Internationalized Domain Names in Applications (IDNA)" 103 | category = "main" 104 | optional = false 105 | python-versions = ">=3.4" 106 | 107 | [[package]] 108 | name = "joblib" 109 | version = "1.0.0" 110 | description = "Lightweight pipelining with Python functions" 111 | category = "main" 112 | optional = false 113 | python-versions = ">=3.6" 114 | 115 | [[package]] 116 | name = "more-itertools" 117 | version = "8.6.0" 118 | description = "More routines for operating on iterables, beyond itertools" 119 | category = "dev" 120 | optional = false 121 | python-versions = ">=3.5" 122 | 123 | [[package]] 124 | name = "numpy" 125 | version = "1.19.5" 126 | description = "NumPy is the fundamental package for array computing with Python." 127 | category = "main" 128 | optional = false 129 | python-versions = ">=3.6" 130 | 131 | [[package]] 132 | name = "packaging" 133 | version = "20.8" 134 | description = "Core utilities for Python packages" 135 | category = "dev" 136 | optional = false 137 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 138 | 139 | [package.dependencies] 140 | pyparsing = ">=2.0.2" 141 | 142 | [[package]] 143 | name = "pluggy" 144 | version = "0.13.1" 145 | description = "plugin and hook calling mechanisms for python" 146 | category = "dev" 147 | optional = false 148 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 149 | 150 | [package.extras] 151 | dev = ["pre-commit", "tox"] 152 | 153 | [[package]] 154 | name = "py" 155 | version = "1.10.0" 156 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 157 | category = "dev" 158 | optional = false 159 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 160 | 161 | [[package]] 162 | name = "pygments" 163 | version = "2.7.4" 164 | description = "Pygments is a syntax highlighting package written in Python." 165 | category = "main" 166 | optional = false 167 | python-versions = ">=3.5" 168 | 169 | [[package]] 170 | name = "pyparsing" 171 | version = "2.4.7" 172 | description = "Python parsing module" 173 | category = "dev" 174 | optional = false 175 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 176 | 177 | [[package]] 178 | name = "pytest" 179 | version = "5.4.3" 180 | description = "pytest: simple powerful testing with Python" 181 | category = "dev" 182 | optional = false 183 | python-versions = ">=3.5" 184 | 185 | [package.dependencies] 186 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 187 | attrs = ">=17.4.0" 188 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 189 | more-itertools = ">=4.0.0" 190 | packaging = "*" 191 | pluggy = ">=0.12,<1.0" 192 | py = ">=1.5.0" 193 | wcwidth = "*" 194 | 195 | [package.extras] 196 | checkqa-mypy = ["mypy (==v0.761)"] 197 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 198 | 199 | [[package]] 200 | name = "rfc3986" 201 | version = "1.4.0" 202 | description = "Validating URI References per RFC 3986" 203 | category = "main" 204 | optional = false 205 | python-versions = "*" 206 | 207 | [package.dependencies] 208 | idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} 209 | 210 | [package.extras] 211 | idna2008 = ["idna"] 212 | 213 | [[package]] 214 | name = "rich" 215 | version = "9.8.1" 216 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 217 | category = "main" 218 | optional = false 219 | python-versions = ">=3.6,<4.0" 220 | 221 | [package.dependencies] 222 | colorama = ">=0.4.0,<0.5.0" 223 | commonmark = ">=0.9.0,<0.10.0" 224 | pygments = ">=2.6.0,<3.0.0" 225 | typing-extensions = ">=3.7.4,<4.0.0" 226 | 227 | [package.extras] 228 | jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] 229 | 230 | [[package]] 231 | name = "scikit-learn" 232 | version = "0.24.0" 233 | description = "A set of python modules for machine learning and data mining" 234 | category = "main" 235 | optional = false 236 | python-versions = ">=3.6" 237 | 238 | [package.dependencies] 239 | joblib = ">=0.11" 240 | numpy = ">=1.13.3" 241 | scipy = ">=0.19.1" 242 | threadpoolctl = ">=2.0.0" 243 | 244 | [package.extras] 245 | benchmark = ["matplotlib (>=2.1.1)", "pandas (>=0.25.0)", "memory-profiler (>=0.57.0)"] 246 | docs = ["matplotlib (>=2.1.1)", "scikit-image (>=0.13)", "pandas (>=0.25.0)", "seaborn (>=0.9.0)", "memory-profiler (>=0.57.0)", "sphinx (>=3.2.0)", "sphinx-gallery (>=0.7.0)", "numpydoc (>=1.0.0)", "Pillow (>=7.1.2)", "sphinx-prompt (>=1.3.0)"] 247 | examples = ["matplotlib (>=2.1.1)", "scikit-image (>=0.13)", "pandas (>=0.25.0)", "seaborn (>=0.9.0)"] 248 | tests = ["matplotlib (>=2.1.1)", "scikit-image (>=0.13)", "pandas (>=0.25.0)", "pytest (>=5.0.1)", "pytest-cov (>=2.9.0)", "flake8 (>=3.8.2)", "mypy (>=0.770)", "pyamg (>=4.0.0)"] 249 | 250 | [[package]] 251 | name = "scipy" 252 | version = "1.6.0" 253 | description = "SciPy: Scientific Library for Python" 254 | category = "main" 255 | optional = false 256 | python-versions = ">=3.7" 257 | 258 | [package.dependencies] 259 | numpy = ">=1.16.5" 260 | 261 | [[package]] 262 | name = "sniffio" 263 | version = "1.2.0" 264 | description = "Sniff out which async library your code is running under" 265 | category = "main" 266 | optional = false 267 | python-versions = ">=3.5" 268 | 269 | [[package]] 270 | name = "sqlalchemy" 271 | version = "1.3.22" 272 | description = "Database Abstraction Library" 273 | category = "main" 274 | optional = false 275 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 276 | 277 | [package.extras] 278 | mssql = ["pyodbc"] 279 | mssql_pymssql = ["pymssql"] 280 | mssql_pyodbc = ["pyodbc"] 281 | mysql = ["mysqlclient"] 282 | oracle = ["cx-oracle"] 283 | postgresql = ["psycopg2"] 284 | postgresql_pg8000 = ["pg8000"] 285 | postgresql_psycopg2binary = ["psycopg2-binary"] 286 | postgresql_psycopg2cffi = ["psycopg2cffi"] 287 | pymysql = ["pymysql"] 288 | 289 | [[package]] 290 | name = "threadpoolctl" 291 | version = "2.1.0" 292 | description = "threadpoolctl" 293 | category = "main" 294 | optional = false 295 | python-versions = ">=3.5" 296 | 297 | [[package]] 298 | name = "typer" 299 | version = "0.3.2" 300 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 301 | category = "main" 302 | optional = false 303 | python-versions = ">=3.6" 304 | 305 | [package.dependencies] 306 | click = ">=7.1.1,<7.2.0" 307 | 308 | [package.extras] 309 | test = ["pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.782)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)", "shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)"] 310 | all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] 311 | dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] 312 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] 313 | 314 | [[package]] 315 | name = "typing-extensions" 316 | version = "3.7.4.3" 317 | description = "Backported and Experimental Type Hints for Python 3.5+" 318 | category = "main" 319 | optional = false 320 | python-versions = "*" 321 | 322 | [[package]] 323 | name = "wcwidth" 324 | version = "0.2.5" 325 | description = "Measures the displayed width of unicode strings in a terminal" 326 | category = "dev" 327 | optional = false 328 | python-versions = "*" 329 | 330 | [metadata] 331 | lock-version = "1.1" 332 | python-versions = "^3.9" 333 | content-hash = "2c10579b56b2fb69963b54afe4fa03d783167ec338f4fd4d70e25d940993f5f2" 334 | 335 | [metadata.files] 336 | atomicwrites = [ 337 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 338 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 339 | ] 340 | attrs = [ 341 | {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, 342 | {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, 343 | ] 344 | certifi = [ 345 | {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, 346 | {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, 347 | ] 348 | click = [ 349 | {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, 350 | {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, 351 | ] 352 | colorama = [ 353 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 354 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 355 | ] 356 | commonmark = [ 357 | {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, 358 | {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, 359 | ] 360 | h11 = [ 361 | {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, 362 | {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, 363 | ] 364 | httpcore = [ 365 | {file = "httpcore-0.12.2-py3-none-any.whl", hash = "sha256:420700af11db658c782f7e8fda34f9dcd95e3ee93944dd97d78cb70247e0cd06"}, 366 | {file = "httpcore-0.12.2.tar.gz", hash = "sha256:dd1d762d4f7c2702149d06be2597c35fb154c5eff9789a8c5823fbcf4d2978d6"}, 367 | ] 368 | httpx = [ 369 | {file = "httpx-0.16.1-py3-none-any.whl", hash = "sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b"}, 370 | {file = "httpx-0.16.1.tar.gz", hash = "sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537"}, 371 | ] 372 | idna = [ 373 | {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"}, 374 | {file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"}, 375 | ] 376 | joblib = [ 377 | {file = "joblib-1.0.0-py3-none-any.whl", hash = "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f"}, 378 | {file = "joblib-1.0.0.tar.gz", hash = "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24"}, 379 | ] 380 | more-itertools = [ 381 | {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"}, 382 | {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"}, 383 | ] 384 | numpy = [ 385 | {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, 386 | {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"}, 387 | {file = "numpy-1.19.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea"}, 388 | {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140"}, 389 | {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d"}, 390 | {file = "numpy-1.19.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76"}, 391 | {file = "numpy-1.19.5-cp36-cp36m-win32.whl", hash = "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a"}, 392 | {file = "numpy-1.19.5-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827"}, 393 | {file = "numpy-1.19.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f"}, 394 | {file = "numpy-1.19.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f"}, 395 | {file = "numpy-1.19.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c"}, 396 | {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080"}, 397 | {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d"}, 398 | {file = "numpy-1.19.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28"}, 399 | {file = "numpy-1.19.5-cp37-cp37m-win32.whl", hash = "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7"}, 400 | {file = "numpy-1.19.5-cp37-cp37m-win_amd64.whl", hash = "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d"}, 401 | {file = "numpy-1.19.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e"}, 402 | {file = "numpy-1.19.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c"}, 403 | {file = "numpy-1.19.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94"}, 404 | {file = "numpy-1.19.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff"}, 405 | {file = "numpy-1.19.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c"}, 406 | {file = "numpy-1.19.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc"}, 407 | {file = "numpy-1.19.5-cp38-cp38-win32.whl", hash = "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2"}, 408 | {file = "numpy-1.19.5-cp38-cp38-win_amd64.whl", hash = "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa"}, 409 | {file = "numpy-1.19.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd"}, 410 | {file = "numpy-1.19.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa"}, 411 | {file = "numpy-1.19.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8"}, 412 | {file = "numpy-1.19.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371"}, 413 | {file = "numpy-1.19.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb"}, 414 | {file = "numpy-1.19.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60"}, 415 | {file = "numpy-1.19.5-cp39-cp39-win32.whl", hash = "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e"}, 416 | {file = "numpy-1.19.5-cp39-cp39-win_amd64.whl", hash = "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e"}, 417 | {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"}, 418 | {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"}, 419 | ] 420 | packaging = [ 421 | {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, 422 | {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, 423 | ] 424 | pluggy = [ 425 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 426 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 427 | ] 428 | py = [ 429 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, 430 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, 431 | ] 432 | pygments = [ 433 | {file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"}, 434 | {file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"}, 435 | ] 436 | pyparsing = [ 437 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 438 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 439 | ] 440 | pytest = [ 441 | {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, 442 | {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, 443 | ] 444 | rfc3986 = [ 445 | {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, 446 | {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, 447 | ] 448 | rich = [ 449 | {file = "rich-9.8.1-py3-none-any.whl", hash = "sha256:7e594114b109dfcf4c242a5ae1b2767dbc49d0abc9c5f082eb7e558dd622bc90"}, 450 | {file = "rich-9.8.1.tar.gz", hash = "sha256:0ec853f882613e75a5e46d545ddaa48cad235c616eaeb094792012fe22e8b2c6"}, 451 | ] 452 | scikit-learn = [ 453 | {file = "scikit-learn-0.24.0.tar.gz", hash = "sha256:076369634ee72b5a5941440661e2f306ff4ac30903802dc52031c7e9199ac640"}, 454 | {file = "scikit_learn-0.24.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:890d7d588f65acb0c4f6c083347c9076916bda5e6bd8400f06244b1afc1009af"}, 455 | {file = "scikit_learn-0.24.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:e534f5f3796db6781c87e9835dcd51b7854c8c5a379c9210b93605965c1941fd"}, 456 | {file = "scikit_learn-0.24.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:d7fe05fcb44eadd6d6c874c768f085f5de1239db3a3b7be4d3d23d12e4120589"}, 457 | {file = "scikit_learn-0.24.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:7f654befc5ad413690cc58f3f34a3e906caf825195ce0fda00a8e9565e1403e6"}, 458 | {file = "scikit_learn-0.24.0-cp36-cp36m-win32.whl", hash = "sha256:afeb06dc69847927634e58579b9cdc72e1390b79497336b2324b1b173f33bd47"}, 459 | {file = "scikit_learn-0.24.0-cp36-cp36m-win_amd64.whl", hash = "sha256:26f66b3726b54dfb76ea51c5d9c2431ed17ebc066cb4527662b9e851a3e7ba61"}, 460 | {file = "scikit_learn-0.24.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c08b27cb78ee8d2dc781a7affed09859441f5b624f9f92da59ac0791c8774dfc"}, 461 | {file = "scikit_learn-0.24.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:905d8934d1e27a686698864a5863ff2c0e13a2ae1adb78a8a848aacc8a49927d"}, 462 | {file = "scikit_learn-0.24.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d819d625832fb2969911a243e009cfa135cb8ef1e150866e417d6e9d75290087"}, 463 | {file = "scikit_learn-0.24.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18f7131e62265bf2691ed1d0303c640313894ccfe4278427478c6b2f45094b53"}, 464 | {file = "scikit_learn-0.24.0-cp37-cp37m-win32.whl", hash = "sha256:b0d13fd56d26cf3de0314a4fd48037108c638fe126d813f5c1222bb0f08b6a76"}, 465 | {file = "scikit_learn-0.24.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c912247e42114f389858ae05d63f4359d4e667ea72aaabee191aee9ad3f9774a"}, 466 | {file = "scikit_learn-0.24.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:758619e49cd7c17282e6cc60d5cc73c02c072b47c9a10010bb3bb47e0d976e50"}, 467 | {file = "scikit_learn-0.24.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:66f27bf21202a850bcd7b6303916e4907f6e22ec59a14974ede4955aed5c7ed0"}, 468 | {file = "scikit_learn-0.24.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5e6e3c042cea83f2e20a45e563b8eabc1f8f72446251fe23ebefdf111a173a33"}, 469 | {file = "scikit_learn-0.24.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2a5348585aa793bc8cc5a72f8e9067c9380834b0aadbd55f924843b071f13282"}, 470 | {file = "scikit_learn-0.24.0-cp38-cp38-win32.whl", hash = "sha256:743b6edd98c98991be46c08e6b21df3861d5ae915f91d59f988384d93f7263e7"}, 471 | {file = "scikit_learn-0.24.0-cp38-cp38-win_amd64.whl", hash = "sha256:2951f87d35e72f007701c6e028aa230f6df6212a3194677c0c950486066a454d"}, 472 | {file = "scikit_learn-0.24.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:44e452ea8491225c5783d49577aad0f36202dfd52aec7f82c0fdfe5fbd5f7400"}, 473 | {file = "scikit_learn-0.24.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:800aaf63f8838c00e85db2267dd226f89858594843fd03932a9eda95746d2c40"}, 474 | {file = "scikit_learn-0.24.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:3eeff086f7329521d27249a082ea3c48c085cedb110db5f65968ab55c3ba2e09"}, 475 | {file = "scikit_learn-0.24.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4395e91b3548005f4a645018435b5a94f8cce232b5b70753020e606c6a750656"}, 476 | {file = "scikit_learn-0.24.0-cp39-cp39-win32.whl", hash = "sha256:80ca024154b84b6ac4cfc86930ba13fdc348a209753bf2c16129db6f9eb8a80b"}, 477 | {file = "scikit_learn-0.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:490436b44b3a1957cb625e871764b0aa330b34cc416aea4abc6c38ca63d0d682"}, 478 | ] 479 | scipy = [ 480 | {file = "scipy-1.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d4303e3e21d07d9557b26a1707bb9fc065510ee8501c9bf22a0157249a82fd0"}, 481 | {file = "scipy-1.6.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1bc5b446600c4ff7ab36bade47180673141322f0febaa555f1c433fe04f2a0e3"}, 482 | {file = "scipy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8840a9adb4ede3751f49761653d3ebf664f25195fdd42ada394ffea8903dd51d"}, 483 | {file = "scipy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8629135ee00cc2182ac8be8e75643b9f02235942443732c2ed69ab48edcb6614"}, 484 | {file = "scipy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:58731bbe0103e96b89b2f41516699db9b63066e4317e31b8402891571f6d358f"}, 485 | {file = "scipy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:876badc33eec20709d4e042a09834f5953ebdac4088d45a4f3a1f18b56885718"}, 486 | {file = "scipy-1.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c0911f3180de343643f369dc5cfedad6ba9f939c2d516bddea4a6871eb000722"}, 487 | {file = "scipy-1.6.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b8af26839ae343655f3ca377a5d5e5466f1d3b3ac7432a43449154fe958ae0e0"}, 488 | {file = "scipy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4f1d9cc977ac6a4a63c124045c1e8bf67ec37098f67c699887a93736961a00ae"}, 489 | {file = "scipy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:eb7928275f3560d47e5538e15e9f32b3d64cd30ea8f85f3e82987425476f53f6"}, 490 | {file = "scipy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:31ab217b5c27ab429d07428a76002b33662f98986095bbce5d55e0788f7e8b15"}, 491 | {file = "scipy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:2f1c2ebca6fd867160e70102200b1bd07b3b2d31a3e6af3c58d688c15d0d07b7"}, 492 | {file = "scipy-1.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:155225621df90fcd151e25d51c50217e412de717475999ebb76e17e310176981"}, 493 | {file = "scipy-1.6.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f68d5761a2d2376e2b194c8e9192bbf7c51306ca176f1a0889990a52ef0d551f"}, 494 | {file = "scipy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d902d3a5ad7f28874c0a82db95246d24ca07ad932741df668595fe00a4819870"}, 495 | {file = "scipy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:aef3a2dbc436bbe8f6e0b635f0b5fe5ed024b522eee4637dbbe0b974129ca734"}, 496 | {file = "scipy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:cdbc47628184a0ebeb5c08f1892614e1bd4a51f6e0d609c6eed253823a960f5b"}, 497 | {file = "scipy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:313785c4dab65060f9648112d025f6d2fec69a8a889c714328882d678a95f053"}, 498 | {file = "scipy-1.6.0.tar.gz", hash = "sha256:cb6dc9f82dfd95f6b9032a8d7ea70efeeb15d5b5fd6ed4e8537bb3c673580566"}, 499 | ] 500 | sniffio = [ 501 | {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, 502 | {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, 503 | ] 504 | sqlalchemy = [ 505 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:61628715931f4962e0cdb2a7c87ff39eea320d2aa96bd471a3c293d146f90394"}, 506 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:81d8d099a49f83111cce55ec03cc87eef45eec0d90f9842b4fc674f860b857b0"}, 507 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:d055ff750fcab69ca4e57b656d9c6ad33682e9b8d564f2fbe667ab95c63591b0"}, 508 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-win32.whl", hash = "sha256:9bf572e4f5aa23f88dd902f10bb103cb5979022a38eec684bfa6d61851173fec"}, 509 | {file = "SQLAlchemy-1.3.22-cp27-cp27m-win_amd64.whl", hash = "sha256:7d4b8de6bb0bc736161cb0bbd95366b11b3eb24dd6b814a143d8375e75af9990"}, 510 | {file = "SQLAlchemy-1.3.22-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4a84c7c7658dd22a33dab2e2aa2d17c18cb004a42388246f2e87cb4085ef2811"}, 511 | {file = "SQLAlchemy-1.3.22-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:f1e88b30da8163215eab643962ae9d9252e47b4ea53404f2c4f10f24e70ddc62"}, 512 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f115150cc4361dd46153302a640c7fa1804ac207f9cc356228248e351a8b4676"}, 513 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6aaa13ee40c4552d5f3a59f543f0db6e31712cc4009ec7385407be4627259d41"}, 514 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3ab5b44a07b8c562c6dcb7433c6a6c6e03266d19d64f87b3333eda34e3b9936b"}, 515 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:426ece890153ccc52cc5151a1a0ed540a5a7825414139bb4c95a868d8da54a52"}, 516 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-win32.whl", hash = "sha256:bd4b1af45fd322dcd1fb2a9195b4f93f570d1a5902a842e3e6051385fac88f9c"}, 517 | {file = "SQLAlchemy-1.3.22-cp35-cp35m-win_amd64.whl", hash = "sha256:62285607a5264d1f91590abd874d6a498e229d5840669bd7d9f654cfaa599bd0"}, 518 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:314f5042c0b047438e19401d5f29757a511cfc2f0c40d28047ca0e4c95eabb5b"}, 519 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:62fb881ba51dbacba9af9b779211cf9acff3442d4f2993142015b22b3cd1f92a"}, 520 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bde677047305fe76c7ee3e4492b545e0018918e44141cc154fe39e124e433991"}, 521 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:0c6406a78a714a540d980a680b86654feadb81c8d0eecb59f3d6c554a4c69f19"}, 522 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-win32.whl", hash = "sha256:95bde07d19c146d608bccb9b16e144ec8f139bcfe7fd72331858698a71c9b4f5"}, 523 | {file = "SQLAlchemy-1.3.22-cp36-cp36m-win_amd64.whl", hash = "sha256:888d5b4b5aeed0d3449de93ea80173653e939e916cc95fe8527079e50235c1d2"}, 524 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:d53f59744b01f1440a1b0973ed2c3a7de204135c593299ee997828aad5191693"}, 525 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:70121f0ae48b25ef3e56e477b88cd0b0af0e1f3a53b5554071aa6a93ef378a03"}, 526 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:54da615e5b92c339e339fe8536cce99fe823b6ed505d4ea344852aefa1c205fb"}, 527 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:68428818cf80c60dc04aa0f38da20ad39b28aba4d4d199f949e7d6e04444ea86"}, 528 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-win32.whl", hash = "sha256:17610d573e698bf395afbbff946544fbce7c5f4ee77b5bcb1f821b36345fae7a"}, 529 | {file = "SQLAlchemy-1.3.22-cp37-cp37m-win_amd64.whl", hash = "sha256:216ba5b4299c95ed179b58f298bda885a476b16288ab7243e89f29f6aeced7e0"}, 530 | {file = "SQLAlchemy-1.3.22-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0c72b90988be749e04eff0342dcc98c18a14461eb4b2ad59d611b57b31120f90"}, 531 | {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:491fe48adc07d13e020a8b07ef82eefc227003a046809c121bea81d3dbf1832d"}, 532 | {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f8191fef303025879e6c3548ecd8a95aafc0728c764ab72ec51a0bdf0c91a341"}, 533 | {file = "SQLAlchemy-1.3.22-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:108580808803c7732f34798eb4a329d45b04c562ed83ee90f09f6a184a42b766"}, 534 | {file = "SQLAlchemy-1.3.22-cp38-cp38-win32.whl", hash = "sha256:bab5a1e15b9466a25c96cda19139f3beb3e669794373b9ce28c4cf158c6e841d"}, 535 | {file = "SQLAlchemy-1.3.22-cp38-cp38-win_amd64.whl", hash = "sha256:318b5b727e00662e5fc4b4cd2bf58a5116d7c1b4dd56ffaa7d68f43458a8d1ed"}, 536 | {file = "SQLAlchemy-1.3.22-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:1418f5e71d6081aa1095a1d6b567a562d2761996710bdce9b6e6ba20a03d0864"}, 537 | {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:5a7f224cdb7233182cec2a45d4c633951268d6a9bcedac37abbf79dd07012aea"}, 538 | {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:715b34578cc740b743361f7c3e5f584b04b0f1344f45afc4e87fbac4802eb0a0"}, 539 | {file = "SQLAlchemy-1.3.22-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:2ff132a379838b1abf83c065be54cef32b47c987aedd06b82fc76476c85225eb"}, 540 | {file = "SQLAlchemy-1.3.22-cp39-cp39-win32.whl", hash = "sha256:c389d7cc2b821853fb018c85457da3e7941db64f4387720a329bc7ff06a27963"}, 541 | {file = "SQLAlchemy-1.3.22-cp39-cp39-win_amd64.whl", hash = "sha256:04f995fcbf54e46cddeb4f75ce9dfc17075d6ae04ac23b2bacb44b3bc6f6bf11"}, 542 | {file = "SQLAlchemy-1.3.22.tar.gz", hash = "sha256:758fc8c4d6c0336e617f9f6919f9daea3ab6bb9b07005eda9a1a682e24a6cacc"}, 543 | ] 544 | threadpoolctl = [ 545 | {file = "threadpoolctl-2.1.0-py3-none-any.whl", hash = "sha256:38b74ca20ff3bb42caca8b00055111d74159ee95c4370882bbff2b93d24da725"}, 546 | {file = "threadpoolctl-2.1.0.tar.gz", hash = "sha256:ddc57c96a38beb63db45d6c159b5ab07b6bced12c45a1f07b2b92f272aebfa6b"}, 547 | ] 548 | typer = [ 549 | {file = "typer-0.3.2-py3-none-any.whl", hash = "sha256:ba58b920ce851b12a2d790143009fa00ac1d05b3ff3257061ff69dbdfc3d161b"}, 550 | {file = "typer-0.3.2.tar.gz", hash = "sha256:5455d750122cff96745b0dec87368f56d023725a7ebc9d2e54dd23dc86816303"}, 551 | ] 552 | typing-extensions = [ 553 | {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, 554 | {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, 555 | {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, 556 | ] 557 | wcwidth = [ 558 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 559 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 560 | ] 561 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "jobsearch" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Petr Stribny "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.9" 9 | scikit-learn = "^0.24.0" 10 | typer = "^0.3.2" 11 | rich = "^9.8.1" 12 | httpx = "^0.16.1" 13 | SQLAlchemy = "^1.3.22" 14 | 15 | [tool.poetry.dev-dependencies] 16 | pytest = "^5.2" 17 | 18 | [build-system] 19 | requires = ["poetry-core>=1.0.0"] 20 | build-backend = "poetry.core.masonry.api" 21 | --------------------------------------------------------------------------------