diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..ceb656a7 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,43 @@ +version: 2.1 + +orbs: + python: circleci/python@1.3.2 + docker: circleci/docker@1.5.0 + +jobs: + build-and-test: + executor: python/default + environment: + TEST: True + steps: + - checkout + - setup_remote_docker + - docker/install-docker-compose + - run: + command: | + python -m pip install --upgrade pip + pip install pylint + pip install -r requirements.txt + pip install scrapy-autounit + name: Install dependencies + - run: + command: | + scrapy crawl espn-live + scrapy crawl espn-players + python3 -m unittest discover autounit/tests/ + name: Crawler Test with autounit + - run: + command: | + pylint app -E + pylint app --exit-zero + name: Lint with pylint + - run: + name: Test with pytest + command: | + docker -v + docker-compose -v + docker-compose -f docker/docker-compose-test.yaml up --build --exit-code-from test +workflows: + main: + jobs: + - build-and-test diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml deleted file mode 100644 index dc6b1694..00000000 --- a/.github/workflows/python-app.yml +++ /dev/null @@ -1,38 +0,0 @@ -# This workflow will install Python dependencies, run tests and lint with a single version of Python -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: Python application - -on: - push: - branches: [ master , 'feature-*'] - pull_request: - branches: [ master , 'feature-*'] - -jobs: - build: - strategy: - matrix: - platform: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.platform }} - - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pylint pytest - pip install -r requirements.txt - - name: Lint with pylint - run: | - # stop the build if there are Python syntax errors or undefined names - pylint app -E - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - pylint app --exit-zero - - name: Test with pytest - run: | - pytest diff --git a/.gitignore b/.gitignore index 1b77d315..0d9261e0 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ instance/ # Scrapy stuff: .scrapy +autounit # Sphinx documentation docs/_build/ diff --git a/Dockerfile.development b/Dockerfile.development deleted file mode 100644 index 3396e521..00000000 --- a/Dockerfile.development +++ /dev/null @@ -1,16 +0,0 @@ - -FROM python:3.8-slim as build - -WORKDIR / - -COPY ./requirements.txt ./requirements.txt -RUN apt-get update && \ - apt-get -y install gcc mono-mcs && \ - rm -rf /var/lib/apt/lists/* -RUN pip install -r requirements.txt - -COPY ./app /app -COPY ./crawler /crawler -COPY ./scrapy.cfg /scrapy.cfg - -ENTRYPOINT ["uvicorn", "app.main:app"] \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 1c750ad8..d15428e2 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ In the past year or so fantasy cricket has been getting a lot of traction and wi 1. [FastAPI](https://fastapi.tiangolo.com/) 2. [sklearn](https://scikit-learn.org/stable/) -3. [pycricbuzz](https://github.com/codophobia/pycricbuzz) +3. [scrapyrt](https://scrapyrt.readthedocs.io/en/stable/) 4. [scrapy](https://docs.scrapy.org/en/latest/) Install using
@@ -16,7 +16,7 @@ Install using
pip3 install -r requirements.txt ``` -## I want to run your project +## Local Development To run our project follow these steps @@ -27,24 +27,57 @@ To run our project follow these steps cd Best11-Fantasycricket ``` -3. Run the model : +3. + **Linux and MACOS** + + 1. Type `nano /etc/hosts` on your terminal or open `/etc/hosts` on your prefered editor + + **Windows** + 1. Open `C:\windows\system32\drivers\etc\hosts` in your prefered editor + + + 2. And add the below line to the the file and save + + `127.0.0.1 espncricinfo` + + **OR** + + 1. Open `app/fantasy_cricket/scrapyrt_client.py` in your prefered editor + + 2. Change line `16` to + + ```python + self.url = "http://localhost:9080/crawl.json" + ``` + +4. Open a tab on your terminal and run `uvicorn app.main:app` -5. `Open http://localhost:8000/` and voila!! +5. Open another tab on your terminal and run + +`scrapyrt` + + +6. Open `http://localhost:8000/` and voila!! + +**Note:** +Visit `http://localhost:9080/crawl.json` with the correct queries to see the crawler api ### Docker 1. Follow the steps: - ```bash - docker build -t best11fantasycricket:latest "." - ``` ```bash - docker-compose up + docker build -t espncricinfo:latest "." -f docker/espncricinfo/Dockerfile + docker build -t best11:latest "." -f docker/11tastic/Dockerfile + docker-compose -f docker/docker-compose.yaml up ``` -2. Visit `http://localhost:8080/` +2. Visit `http://localhost:8080/` to see the website in action + +**Note** + Visit `http://localhost:9080/crawl.json` with the correct queries to see the crawler api ## How do I contribute to this project???? @@ -69,9 +102,9 @@ If you have any questions regarding our project , you can contact any of the mai ### Acknowledgements -1. Special thanks to [scientes](https://github.com/scientes) for setting up the basic webcrawler +1. Special thanks to [scientes](https://github.com/scientes) for allowing us to use the server to host the website -2. We would like to thank [Howstat](http://www.howstat.com/cricket/home.asp) for their amazing website with daily updates and availabilty to scrape +2. We would like to thank [espncricinfo](https://www.espncricinfo.com/) for their amazing website with daily updates and availabilty to scrape If you liked our project we would really appreciate you starring this repo. diff --git a/app/__init__.py b/app/__init__.py old mode 100644 new mode 100755 diff --git a/app/fantasy_cricket/__init__.py b/app/fantasy_cricket/__init__.py old mode 100644 new mode 100755 diff --git a/app/fantasy_cricket/data/flags.json b/app/fantasy_cricket/data/flags.json deleted file mode 100644 index 91df0982..00000000 --- a/app/fantasy_cricket/data/flags.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "countries": { - "India": { - "flag": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAAC3CAMAAAAGjUrGAAAAmVBMVEX/mTMSiAf/////lycAgQAAAIgAAIUAAH4AAIIAAIMAAHwAAHPi4u+kpMvz8/kAAHjn5/Lt7fXd3ey5uddPT6GZmcWDg7n6+v7Ly+HIyOJERJxlZao+PponJ5L29vy0tNbW1uggIJCfn8iGhrsYGJBNTaB2drQQEIx+fripqc5ra62QkMBXV6QiIpFfX6i+vtwyMpw6Opg1NZe0gadDAAAEvUlEQVR4nO3ba3OiSBiG4UzvdDcip8YDiAdQZAIxExP//4/btzHjZH1Nze6HpVPlc1Ul0eiH9g7Q2JKHBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA/+gvuPYg4BqacGjCoQmHJhyacF+hSTI5LFd7uV8tD5PE9WDEF2gSHNbaGyklpVRq5Omfh8D1kBw3qaZ6pCiGVFrr/oYc6SZ3OyinTZIpdZCeevnhNyY1jf/jRXm0vejG6S7kssnObhmeNxMbcTSiEuZIt2aeZ7eancNxOWzy7NFrn3cnI/p9pbTfcmFO3ZxaeVN3A3PWJNrS5lAEmb3d5wgvt7KgoMe2kauhuWoSPdK2QHvIwt5ZpPTtQF/p+S7tVbQNPbqK4qhJRluJ17VGZB3di2yPF/o62AxdJkzbUZRt5mZwjpo0lOTY3wrtFGMPHq/vP5N+JxJHiuLomOKmycyTXinOUewLX1IYSTmW7/cpiSjpOTMno3PSJNA049TnQ4gIae8J6cVrSkWbSNdvJvRITbOPdnJO66RJo1QzM3RjaY8YMhLVVmR+JraViGhzEZn9vZnZp7kYnosmCy31+UR180oH1a4QWWyiODJxJhqajaPTpn80oee5OM130WSlVL3pZ11hJO0daiLWITUJ12KiaM+Spn9ssamVKhyMz0GTVNsDrOi6/o5fiVKLdmVis2qFLkXl27OV/rhCh1mdDj9AB01qmoftnz+Z2ir5+Cjk08JP49RfPElxHNvdpZvao2vhyVE9/AAdNHmkOfZ8ihpK2l4m4y4fJ7ryKp2M8248oe1D7vrHDc3Z++EHOHwTmojlr9tRISciHB/beZ3v87puj+OdmMiV+fUE6WI6Hr5J6amnaFEuzptK6av8GAfrKmzDah3Ex/zR798Iik1e5tGT8srBRzh8k7lSLf1ID9NlWNF5yHLcBuugLd6KNl0H87E9N0m7p+Zgj66tUvPBRzh8k2cl/fNZRzLbxut2kczXaWFXIEdFsG6TfH6KH2fnPSb3pXoefITDN3lVanpZBTDhytd12o6kNWrTg/ZPu8vKYzRV6nXwEQ7fRErvkiSLIpNWeViovokqdnmVmii6LBJE3u8D8mCcNEmqYzhbnqT2/TiOvX37Js/e6q1Hv4l9LX8+H8IyT++mCbaTK1fHkzccTz7OO8Hveafp550mWM/vct6Zn085cH7ywe3z2JTOY9O7PY/F+50b9vS++PyiP3tfPLm398WiHv15/aTs109Wd7N+gnW2G4p/uR6b3c96rMiv1u2bT9btgztat+8/3zmY989xbn6+I+7t8x18DngLPi++YYrrChhcf3IDrlO6IdqOpCpSXM/2D/11j234yXWPwy+bXOD6WO5rXkc9vdvrqAWut78N/5dx08f/3zF/fvr/7is0+WrQhEMTDk04NOHQhHv4DtcevsE1NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMTDk04NOHQhEMT7m/cNifkUwM0UQAAAABJRU5ErkJggg==" - }, - "England": { - "flag": "data:image/png;base64,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" - }, - "Australia": { - "flag": "data:image/png;base64,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" - }, - "Bangladesh": { - "flag": "data:image/jpeg;base64,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" - }, - "New Zealand": { - "flag": "data:image/png;base64,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" - }, - "South Africa": { - "flag": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAAC3CAMAAAAGjUrGAAAA3lBMVEUAd0ngPDEAFIn///8AAAD/uBwAdEVQkXAAAH7eHQUAdEv/uhr/vR0AcksAdkoAbTj/wB0AAIPUqyf0sBvgOCwAcj/t9fJfnYDeKhtiRwvk7+oACYfOqSkUekfZrCb2+/m20cV4jzunyLjAihVpTAuFkjlBjmu4hRQrfUXvrRqNlTfhRTv42dfeJRRVmXnjU0r64+LvpaFaQQrY2upupYvtl5PjVEuty73wqaX76eicwa82imROOAiZncfh4u81PpgrNZWOksEhLJITIY5/hLn20M7skYzkXVWufRPGyeByxTLcAAAFhUlEQVR4nO2diVbUQBBF25jEZdARdcQdN1TcFXAXxN3//yETWseQ7iZJVy/V1fW+wHPPPTcFDhMhBlZvrx/X7Pvjy8eobgiJEIv6lg7K8R9PL8b+x3vaMBMh5mtaVZ5QVWUMk9xUGcUkM1VGMslKlbFMclJlPBOxWGSiygQmZlWe01JlEhNjVXZOUVJlGpM8qjKVibEqO3SqMplJBlWxYEK+KjZMGlWeUa6KHRPaVbFkQroq1kxEbVIl+arYM2mrckkH5UXqqkCYUK0KiElbFa0qaVdFnD4LVOUNuaqI6zdOA1XRVyVhVcSsun0GqEptUCXVqoiqml0XYFW0t8qLRM/ahkk1m4FVmVNSpWVSVSu+qpLkrSKZVA6qYnoApafKXyZtVViVv1syaasCvVWIVOU/E59VeZ2UKl0mHqvyMqWz9hATfgAdrMfEpyrJVKXPhFXRMeGqiAsrKpWV82BV7if8ABLl3XNaVYC3Sp1wVURZ3qs0quR81jZMSr0q4B+Wa8NZi16VAyblPW1V4KqcSLIqkok3VRZ3ElTlHxNvVTGogrkqSyb+qpKcKh0moavyE6sqXSYGVaqNzKpymImhKpmdtT0mpqpsQM9aU1UuI1RFYaKvigNV0rlVVCZGVbKpio5JeSXoD8vvsD2AtEwyr4qBibeq3L+qrQoqVUxMvFXFoMpbRL+CMzMxVeWhH1UQVeUIJqEfQG+xPICOZKJXhXxVjmYSvCooVBli4u9WwVuVQSZl+cBTVd5jVWUEE39V0Z+1r09F3hgmoc/a2BvHJPCtEnkjmXirylxflagbzSTwrRJz45m0qsy8VAWbKlOY5FKVSUwyqcpEJuUrbVVugqvyAZEqU5k0quigkKrKdCYGVQg9gCyYGB9AVKpixUSvyspN8AMIR1XsmLRV8XKrLE4gmC0TU1WgqtQIZs3EVJVHwKpgmD2T8tWm9laBqhJ/ACZl+Ut71iavCoiJr7M28mBMGlW0D6BHwAdQ3EGZ6KsCv1ViDsykrQqxB5ADJuVHfVWSVcUFE2pVccOE1q3iiAmpqjhjUn4ko4o7JnSq4pIJlao4ZUKkKo6ZNFVRobSqpATFNZOy3FSZNKqkBMU1EfZEGfekP37uKOP7pD++Y5WRKImcIyJESiLnBgmVksi5IMK/Z1NGqCRyYCL8e3tVElIlkQNKwv8PqEjC/1/cl4TSTdKdPRL+/IkiCX9OSZGEP882RpK8P/fIn48dJYmDj9wn/Dlq/rz9SEmIlESO/35HHf+dlzr+e0B1/Hej6vjvi9Xx36Gr4+8rUIfvey12905GHrrvP/m0eq2IPGTfk7O7txqbyDCTsN+n9GkVAZIBJmFvEhSSFANMwn4/G4KSyKH5Hj8skhRHMQlckpNIJCnMTMJ+LygiSQojk7CHK5qSyCH4nmFckhR6JoEfN4hKIhf7e8vRSVJomGRdErmo70HY/YxPkqLPJOz7MvCVRC7ee1W2EJZELtr7d/aRSlJ0mIR9T9MWzpLIxXmf1z7Gx81yMd77hvEm6S7C+wERl0Qu+Hskkd4k3YV+3yjuksiFfS9tApI0C/r+YvQlkQv4nmvUN0l34d6HnkJJ5ARLokwEkiSNksgJlkTZfybN4Qq8SRamwzWZksgtmXg7XLe+JCVJsWTCJelMcEmUicrFj8Dp3yTdibYkAizJOo2SyAkHJanJlEROcEmUCV+/J0lVkmYwR8R8jchN0h2IyGJxSyvJt4QlKWBMyByuvdkTqWlKUgCYzNdI3STdWRIxliTJw7U3S0loHa69WUlSUy2JnI0kdEsiN10Sso+b5VxJ8pWKJMVUJtRLIudEEjIlkZsiCf2SyMEloVQSudGSZFESOZZEHUuijiVRx5KoG0RSb2sl+U1VkmZ/AMPE8bW5hK63AAAAAElFTkSuQmCC" - }, - "West Indies": { - "flag": "data:image/png;base64,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" - }, - "Pakistan": { - "flag": "data:image/png;base64,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" - }, - "Ireland": { - "flag": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAT4AAACfCAMAAABX0UX9AAAAD1BMVEX///8Wm2L/iD4AklD/fSDTW6TWAAAA/ElEQVR4nO3QMQEAIAwDsA7wr3kq6JVISKblpuadlujTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp0+fPn369OnTp++jBSj2x/+Qiql8AAAAAElFTkSuQmCC" - }, - "Afghanistan": { - "flag": "data:image/png;base64,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" - }, - "Sri Lanka": { - "flag": "data:image/png;base64,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" - } - } -} \ No newline at end of file diff --git a/app/fantasy_cricket/fantasy_leagues.py b/app/fantasy_cricket/fantasy_leagues.py new file mode 100644 index 00000000..7409a656 --- /dev/null +++ b/app/fantasy_cricket/fantasy_leagues.py @@ -0,0 +1,37 @@ +"""This module defines all supported fantasy_leagues and their functionality. +All browsers must inherit from app.fantasy_cricket.Team`. +""" +from app.fantasy_cricket.team import Team + + +class Dream11(Team): + """Dream11 League + + Supported formats: + * ODI + * T20 + * TEST + """ + + name = "Dream11" + + batting_dict = { + "runs": [1, 1, 1], + "boundaries": [1, 1, 1], + "sixes": [2, 2, 2], + "50": [4, 4, 8], + "100": [8, 8, 16], + "duck": [-4, -3, -2], + } + + bowling_dict = { + "wicket": [16, 25, 25], + "4-wicket-haul": [4, 4, 8], + "5-wicket-haul": [8, 8, 16], + "Maiden": [0, 8, 4], + } + + wk_dict = { + "Catch": [8, 8, 8], + "Stump": [12, 12, 12], + } diff --git a/app/fantasy_cricket/matches.py b/app/fantasy_cricket/matches.py new file mode 100755 index 00000000..f2383287 --- /dev/null +++ b/app/fantasy_cricket/matches.py @@ -0,0 +1,56 @@ +""" +The module is defined to get upcoming match data of the next 2 days +""" + +from typing import List +from app.fantasy_cricket.scrapyrt_client import EspnClient + + +class Matches: + """ + A class to get upcoming live match data of the next 2 days + + """ + + def __init__(self) -> None: + + self.espn = EspnClient() + + def get_upcoming_match(self): + """ + Gets current matches dict + """ + matches = [] + for match in self.espn.get_upcoming_dets(): + if match["team1_squad"] != [] and match["team2_squad"] != []: + matches.append( + { + "team1": match["team1"], + "team2": match["team2"], + "flag_team1": "https://a.espncdn.com/i/teamlogos/cricket/500/" + + match["team1_id"] + + ".png", + "flag_team2": "https://a.espncdn.com/i/teamlogos/cricket/500/" + + match["team2_id"] + + ".png", + } + ) + + return matches + + def get_squad_match_type(self, teams: List[str]): + """ + Gets squad and match_class based on teams + """ + + for match in self.espn.get_upcoming_dets(): + + if match["team1"] == teams[0] and match["team2"] == teams[1]: + match_det = { + "team1_squad": match["team1_squad"], + "team2_squad": match["team2_squad"], + "match_type": match["match_id"], + } + break + + return match_det diff --git a/app/fantasy_cricket/scrapyrt_client.py b/app/fantasy_cricket/scrapyrt_client.py new file mode 100755 index 00000000..4091466c --- /dev/null +++ b/app/fantasy_cricket/scrapyrt_client.py @@ -0,0 +1,112 @@ +""" +This module is built as an api wrapper around the scrapyrt process +""" + +from typing import List +import requests + + +class EspnClient: + """ + A simple wrapper built on the scrapyrt api process + """ + + def __init__(self) -> None: + + self.url = "http://espncricinfo:9080/crawl.json" + + def get_upcoming_dets(self): + """ + Gets the upcoming matches from the scrapyrt api + """ + matches = requests.get( + self.url, params={"spider_name": "espn-live", "start_requests": "true"} + ) + + return matches.json()["items"] + + def get_player_dets(self, players: List[str], team: str): + """ + Gets and parses player info from the scrapyrt api + """ + player_data = [] + for player in players: + player_data.append( + ( + requests.get( + self.url, + params={ + "spider_name": "espn-players", + "url": "https://www.espncricinfo.com/ci/content/player/" + + str(player) + + ".html", + }, + ).json()["items"][0], + player, + ) + ) + for player, player_id in player_data: + if not player["role"]: + continue + if "wicketkeeper" in player["role"].lower(): + player["role"] = "wicket-keeper" + elif "allrounder" in player["role"].lower(): + player["role"] = "all-rounder" + elif "bowler" in player["role"].lower(): + player["role"] = "bowler" + else: + player["role"] = "batsman" + player["team"] = team + player["player_id"] = player_id + + player_data = [player for player, _ in player_data if player["role"]] + return player_data + + def get_match_det(self, player_id: str, role: str, match_type: str): + """ + Gets and parses match details got fr0om the scrapyrt api + """ + role_filter_dict = { + "batsman": ["Catch", "Stump", "wicket", "Maiden"], + "bowler": ["Catch", "Stump", "runs", "boundaries", "sixes"], + "all-rounder": ["Catch", "Stump"], + "wicket-keeper": ["wicket", "Maiden"], + } + match_det = requests.get( + self.url, + params={ + "spider_name": "espn-matches", + "url": "https://stats.espncricinfo.com/ci/engine/player/" + + player_id + + ".html?class=" + + match_type + + ";orderby=start;orderbyad=reverse;template=results;type=allround;view=match", + }, + ).json()["items"] + for i, _ in enumerate(match_det): + match_det[i] = { + key: val + for key, val in match_det[i].items() + if key not in role_filter_dict[role] + } + if role in ["batsman", "all-rounder", "wicket-keeper"]: + match_det[i]["100"] = match_det[i]["50"] = match_det[i]["duck"] = 0 + if not match_det[i]["runs"]: + match_det[i]["runs"] = match_det[i]["boundaries"] = match_det[i][ + "sixes" + ] = 0 + elif match_det[i]["runs"] >= 100: + match_det[i]["100"] = 1 + elif match_det[i]["runs"] >= 50: + match_det[i]["50"] = 1 + elif match_det[i]["runs"] == 0: + match_det[i]["duck"] = 1 + if role in ["bowler", "all-rounder"]: + match_det[i]["4-wicket-haul"] = match_det[i]["5-wicket-haul"] = 0 + if not match_det[i]["wicket"]: + match_det[i]["wicket"] = match_det[i]["Maiden"] = 0 + elif match_det[i]["wicket"] >= 5: + match_det[i]["5-wicket-haul"] = 1 + elif match_det[i]["wicket"] >= 4: + match_det[i]["4-wicket-haul"] = 1 + return match_det diff --git a/app/fantasy_cricket/static/bat.png b/app/fantasy_cricket/static/bat.png deleted file mode 100644 index 4a495fb6..00000000 Binary files a/app/fantasy_cricket/static/bat.png and /dev/null differ diff --git a/app/fantasy_cricket/static/fonts/hackerspace.ttf b/app/fantasy_cricket/static/fonts/hackerspace.ttf old mode 100644 new mode 100755 diff --git a/app/fantasy_cricket/static/icon.png b/app/fantasy_cricket/static/icon.png deleted file mode 100644 index a81e1258..00000000 Binary files a/app/fantasy_cricket/static/icon.png and /dev/null differ diff --git a/app/fantasy_cricket/static/loading.gif b/app/fantasy_cricket/static/loading.gif deleted file mode 100644 index 05e1f570..00000000 Binary files a/app/fantasy_cricket/static/loading.gif and /dev/null differ diff --git a/app/fantasy_cricket/static/result1.jpg b/app/fantasy_cricket/static/result1.jpg deleted file mode 100644 index 98f91ca3..00000000 Binary files a/app/fantasy_cricket/static/result1.jpg and /dev/null differ diff --git a/app/fantasy_cricket/static/styles/style.css b/app/fantasy_cricket/static/styles/style.css deleted file mode 100644 index fb294015..00000000 --- a/app/fantasy_cricket/static/styles/style.css +++ /dev/null @@ -1,73 +0,0 @@ -/* This following style sheet is not being used */ -body { - background-image: url("../images/background1.jpg"); -} - -h1 { - text-align: center; - color: white; - font-size: 3em; - margin-top: 2.5em; -} - -h2 { - text-align: center; - color: white; - font-size: 2em; - margin-top: 3em; -} - -form { - margin-top: 1em; - margin-left: 4.5em; -} - -input { - position: absolute; - left: 44%; - border-radius: 5px; - border: 1px solid; - padding: 8px 6px; - line-height: 120%; -} - -.container { - display: grid; - grid-template-columns: auto auto auto auto; - grid-gap: 10px; - grid-row-gap: 40px; - padding: 10px; -} - -input:hover[type="submit"] { - color: white; - box-shadow: 0 5px 15px rgba(145, 92, 182, .4); -} - -.button { - position: absolute; - top: 450px; - left: 710px; -} - -p { - text-align: center; -} - -.custom-select { - width:200px; - position: absolute; - left: 43.5%; - text-align: center; - margin-top: 1.5em; -} - -select { - height:30px; - text-align: center; - border-radius: 3px; - font-family: Arial; - line-height:30px; - font-size: 1em; -} - diff --git a/app/fantasy_cricket/static/styles/style1.css b/app/fantasy_cricket/static/styles/style1.css old mode 100644 new mode 100755 index c714e50c..9e6516ba --- a/app/fantasy_cricket/static/styles/style1.css +++ b/app/fantasy_cricket/static/styles/style1.css @@ -1,5 +1,5 @@ .bgImage { - background-image: url("../result1.jpg") ; + background-image: url("https://user-images.githubusercontent.com/54945757/106836486-448fe600-66bf-11eb-9878-bb8e2237dd91.jpg") ; background-size:cover; position: fixed; left: 0; diff --git a/app/fantasy_cricket/static/styles/style2.css b/app/fantasy_cricket/static/styles/style2.css old mode 100644 new mode 100755 diff --git a/app/fantasy_cricket/team.py b/app/fantasy_cricket/team.py old mode 100644 new mode 100755 index 7587ebea..2135f474 --- a/app/fantasy_cricket/team.py +++ b/app/fantasy_cricket/team.py @@ -1,218 +1,124 @@ """ -This module defines the teams class which outputs the team for the match -and the Predict class which predicts the score based on the role -Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . +This module defines the teams class which is generic over the classes in fantasy_leagues module """ -import json +from typing import List, Optional import numpy as np from sklearn.linear_model import LinearRegression +from app.fantasy_cricket.scrapyrt_client import EspnClient -class Teams: - """A team class which is defined as a base in predicting teams - * **get_match** : a dictionary which maps the match_id of the selected match - to the match name - * **match_id**: match_id of the match chosen by the user - * **match**: Match name of the selected match_id - * **team_dict**: dictionary which maps the team name to count of the players - selected from that team - *param Id: Id passed to select team for a match +class Team: + """ + A generic class built over the league classes in fantasy_leagues.py """ - def __init__(self, path): - self.path = path - self.team_dict = {} - self.player = {} - self.data = {} + name: Optional[str] = None - def get_max_players(self, role_dict, role): - """ - Returns max player for a particular role - :param role_dict : dict of the players mapped to the score for that role - :type dict( str : float) - :param player : dictionary of the selected players for the best11 - :type dict( str: float) - :param role : role of the player - :type str - :rtype: dict( str: float) - """ - if role_dict != {}: - max_score = { - "wk": [list(role_dict.keys())[0]], - "all": [list(role_dict.keys())[0]], - "bat": list(role_dict.keys())[:3], - "bowl": list(role_dict.keys())[:3], - } - team1,team2 = self.team_dict.keys() - names = max_score[role] - for name in names: - if team1 in role_dict[name]["team"]: - self.team_dict[team1] += 1 - elif team2 in role_dict[name]["team"]: - self.team_dict[team2] += 1 - if name not in self.player: - self.player[name] = role_dict[name] - - def get_restofteam(self, role_dict, reduntant, restteam): - """ - Returns the rest of the players who have not yet been chosen for the 11 - :param role_dict : dict of the players mapped to the score for that role - :type dict( str : float) - :param player : dictionary of the selected players for the best11 - :type dict( str: float) - :param redundant : selected players - :type list(str) - :param restteam: dict of the players left to be selected - :type dict( str: float) - :rtype dict( str: float) - """ - for i in role_dict: - if i not in self.player and i not in reduntant: - restteam[i] = role_dict[i] + batting_dict = {} + bowling_dict = {} + wk_dict = {} - restteam = sorted(restteam.items(), key=lambda x: x[1]["score"], reverse=True) - restteam = {i[0]: i[1] for i in restteam} - return restteam + def __init__(self, team1: str, team2: str) -> None: - def get_captain(self): - """ - Returns Captain and Vice Captain for the selected 11 - :param player: dictionary of the selected players for the best11 - :type dict( str : float) - :rtype: tuple(str,str) - """ - self.player = [ - i[0] - for i in sorted( - self.player.items(), key=lambda x: x[1]["score"], reverse=True - ) - ] - captain, vcaptain = self.player[0], self.player[1] - return captain, vcaptain + self.fantasy_team = {team1: [], team2: []} + self.team1 = team1 + self.team2 = team2 + self.espn = EspnClient() - def team(self): + def get_fantasy_team(self): """ - Returns Captain,Vicecaptain and teams for each role - :rtype: tuple( - str, - str, - dict( str : float), - dict( str : float), - dict( str : float), - dict( str : float), - ) + Get the fantasy team predicted including captains """ + vice_captain, captain = self.get_captain_vicecaptain() + for player in self.fantasy_team[self.team1] + self.fantasy_team[self.team2]: + if player == captain: + player["captain"] = "(C)" + elif player == vice_captain: + player["captain"] = "(VC)" + else: + player["captain"] = "" + return self.fantasy_team[self.team1] + self.fantasy_team[self.team2] - self.data = json.load(open(self.path)) - wkteam = batteam = ballteam = allteam = None - position_map = { - "wk": {"var": wkteam}, - "bat": {"var": batteam}, - "bowl": {"var": ballteam}, - "all": {"var": allteam}, - } - for role in position_map: - role_data = dict() - for player in self.data.keys(): - if self.data[player]["team"] not in self.team_dict.keys(): - self.team_dict[self.data[player]["team"]] = 0 - if self.data[player]["role"] == role: - role_data[player] = self.data[player] - position_map[role]["var"] = get_role_team(role_data) - self.get_max_players(position_map[role]["var"], role) - - count = 0 - redundant = [] - # print(position_map,self.team_dict) - maxi = len(self.player) - while count < 11 - maxi: - print(count) - restteam = {} - for role in position_map: - restteam = self.get_restofteam( - position_map[role]["var"], redundant, restteam - ) - new = list(restteam.keys())[0] - if list(self.team_dict.keys())[0] in restteam[new]["team"]: - if self.team_dict[list(self.team_dict.keys())[0]] < 7: - self.player[new] = restteam[new] - count += 1 - self.team_dict[list(self.team_dict.keys())[0]] += 1 - else: - redundant.append(new) - elif list(self.team_dict.keys())[1] in restteam[new]["team"]: - if self.team_dict[list(self.team_dict.keys())[1]] < 7: - self.player[new] = restteam[new] - count += 1 - self.team_dict[list(self.team_dict.keys())[1]] += 1 - else: - redundant.append(new) - # print(self.player) - return self.get_captain() - - -class Predict: - """ - A Predict class which predicts the score of the teams - * **value** : Number of matches to consider so as to predict the next match score - * **date** : Date the match to be predicted is going to be played - * **dates** : Dataframe of dates of all matches played by the player - :type : pandas.DataFrame() - * **result**: predicted score - * **player_name** : file name corresponding to the player name - :param : player : player name - :type : str - :param : role : player role - :type : str - :param : date : Date of the match - : type : str - """ - - value = 5 - - def __init__(self, player, data): - self.data = data - scores = self.get_scores(player) - self.result = self.predict(scores) - - def get_scores(self, player): + def get_captain_vicecaptain(self): """ - Returns scores in chronological order - :param : player : Player name - :type : String - rtype : list + Get captains and vice captains only """ - scores = self.data[player]["scores"] - scores = sorted(scores.items(), key=lambda x: x[0]) - scores = [i[1] for i in scores] - return scores + return sorted( + self.fantasy_team[self.team1] + self.fantasy_team[self.team2], + key=lambda i: i["score"], + )[-2:] - def predict(self, scores): + def get_score(self, role: str, match_type: str, score_info) -> float: """ - Returns the predicted score - :param : scores : A numpy array with the scores of all matches the - player has played - :type : numpy.array() - :rtype : float + Function to predict the fantasy league score of each player """ - + player_scores = {} + for score_data in score_info: + if role == "batsman": + player_scores[score_data["match_id"]] = sum( + self.batting_dict[key][int(match_type) - 1] * int(score_data[key]) + for key in score_data + if key != "match_id" + ) + elif role == "bowler": + player_scores[score_data["match_id"]] = sum( + self.bowling_dict[key][int(match_type) - 1] * int(score_data[key]) + for key in score_data + if key != "match_id" + ) + elif role == "all-rounder": + player_scores[score_data["match_id"]] = sum( + self.batting_dict[key][int(match_type) - 1] * int(score_data[key]) + for key in score_data + if key + not in [ + "match_id", + "wicket", + "Maiden", + "4-wicket-haul", + "5-wicket-haul", + ] + ) + sum( + self.bowling_dict[key][int(match_type) - 1] * int(score_data[key]) + for key in score_data + if key + not in [ + "match_id", + "runs", + "boundaries", + "sixes", + "100", + "50", + "duck", + ] + ) + elif role == "wicket-keeper": + player_scores[score_data["match_id"]] = sum( + self.batting_dict[key][int(match_type) - 1] * int(score_data[key]) + for key in score_data + if key not in ["match_id", "Catch", "Stump"] + ) + sum( + self.wk_dict[key][int(match_type) - 1] * int(score_data[key]) + for key in score_data + if key + not in [ + "match_id", + "runs", + "boundaries", + "sixes", + "100", + "50", + "duck", + ] + ) + scores = [player_scores[k] for k in sorted(player_scores)] regr = LinearRegression(fit_intercept=True) - y_train = np.array(scores[len(scores) - self.value :]).reshape(-1, 1) - x_train = np.array(range(self.value)).reshape(-1, 1) + y_train = np.array(scores).reshape(-1, 1) + x_train = np.array(range(5)).reshape(-1, 1) try: regr.fit(x_train, y_train) - pred = regr.predict(np.array(self.value).reshape(1, -1)) + pred = regr.predict(np.array(5).reshape(1, -1)) if pred[0][0] < 0: result = 0 else: @@ -221,28 +127,74 @@ def predict(self, scores): result = -1 return result + def get_min_team(self, players): + """ + Gets the min team as per the fantasy league + """ + role_score_map = { + "batsman": sorted( + [player for player in players if player["role"] == "batsman"], + key=lambda i: i["score"], + )[-3:], + "bowler": sorted( + [player for player in players if player["role"] == "bowler"], + key=lambda i: i["score"], + )[-3:], + "all-rounder": sorted( + [player for player in players if player["role"] == "all-rounder"], + key=lambda i: i["score"], + reverse=True, + )[0:1], + "wicket-keeper": sorted( + [player for player in players if player["role"] == "wicket-keeper"], + key=lambda i: i["score"], + reverse=True, + )[:1], + } -def get_role_team(data): - """ - Returns the player names and score corresponding to a role - from the files dataframe - :param role : role of the players in the dataframe - :type str - :param role_data : dataframe of the list of players and files - :type pandas.DataFrame() - :param file_name : file name of the match - :type str - :rtype: dict(str: float) - """ - - team = {} + for role in role_score_map: + for player in role_score_map[role]: + if player["team"] == self.team1: + self.fantasy_team[self.team1].append(player) + elif player["team"] == self.team2: + self.fantasy_team[self.team2].append(player) - for player_det in data.keys(): - if player_det not in team: - team[player_det] = {} - team[player_det]["score"] = Predict(player_det, data).result - team[player_det]["team"] = data[player_det]["team"] - wkt = sorted(team.items(), key=lambda x: x[1]["score"], reverse=True) - wkteam = {i[0]: i[1] for i in wkt} + return self.fantasy_team[self.team1] + self.fantasy_team[self.team2] - return wkteam + def fetch_fantasy_team( + self, player_team1: List[str], player_team2: List[str], match_type: str + ): + """ + Builds the fantasy Team + """ + players = self.espn.get_player_dets(player_team1, self.team1) + players += self.espn.get_player_dets(player_team2, self.team2) + remove = [] + for player in players: + score_info = self.espn.get_match_det( + player["player_id"], player["role"], match_type + ) + if len(score_info) == 5: + player["score"] = self.get_score(player["role"], match_type, score_info) + else: + remove.append(player) + + for player in remove: + players.remove(player) + + remove = self.get_min_team(players) + + for player in remove: + players.remove(player) + + for player in sorted(players, key=lambda i: i["score"]): + if ( + len(self.fantasy_team[self.team1]) + len(self.fantasy_team[self.team2]) + ) == 11: + break + if player["team"] == self.team1: + if len(self.fantasy_team[self.team1]) != 7: + self.fantasy_team[self.team1].append(player) + elif player["team"] == self.team2: + if len(self.fantasy_team[self.team2]) != 7: + self.fantasy_team[self.team2].append(player) diff --git a/app/fantasy_cricket/templates/Playing_11.html b/app/fantasy_cricket/templates/Playing_11.html old mode 100644 new mode 100755 index 5647a46c..2be75cbb --- a/app/fantasy_cricket/templates/Playing_11.html +++ b/app/fantasy_cricket/templates/Playing_11.html @@ -1,25 +1,10 @@ - Choose your playing 11 - + Select 11 Players From Each Team

-
+
@@ -45,10 +30,10 @@

{% for player in squads[0] %} - {{player}} + {{player['name']}} - + {% endfor %} @@ -66,10 +51,10 @@

{% for player in squads[1] %} - {{player}} + {{player['name']}} - + {% endfor %} @@ -85,7 +70,7 @@

- Loading + Loading

Loading....

diff --git a/app/fantasy_cricket/templates/index.html b/app/fantasy_cricket/templates/index.html old mode 100644 new mode 100755 index 5340b9d4..520fc272 --- a/app/fantasy_cricket/templates/index.html +++ b/app/fantasy_cricket/templates/index.html @@ -1,19 +1,4 @@ - @@ -24,13 +9,12 @@ - Fantasy-cricket Prediction - + - + Results Page @@ -48,140 +33,29 @@

Your Recommended Team

- - - -
-
-
- -
{{ t6 }}{{ c6 }}{{ v6 }}
-
-
-
- -
{{ t7 }}{{ c7 }}{{ v7 }}
-
-
-
- -
{{ t8 }}{{ c8 }}{{ v8 }}
-
-
-
- -
{{ t9 }}{{ c9 }}{{ v9 }}
-
-
-
- -
{{ t10 }}{{ c10 }}{{ v10 }}
-
+

+ {% for player in team[5:] %}
-
{{ t11 }}{{ c11 }}{{ v11 }}
+
{{ player["name"] }}{{ player["captain"] }}
+ {% endfor %}
- -
+ {% for player in team[0:5] %}
-
{{ t1 }}{{ c1 }}{{ v1 }}
-
-
-
- -
{{ t2 }}{{ c2 }}{{ v2 }}
-
-
- -
{{ t3 }}{{ c3 }}{{ v3 }}
-
-
-
- -
{{ t4 }}{{ c4 }}{{ v4 }}
-
-
-
- -
{{ t5 }}{{ c5 }}{{ v5 }}
+
{{ player["name"] }}{{ player["captain"] }}
+ + {% endfor %}
diff --git a/app/fantasy_cricket/utils.py b/app/fantasy_cricket/utils.py deleted file mode 100644 index 207bf642..00000000 --- a/app/fantasy_cricket/utils.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Module which helps to get current match data -Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. -- - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" - -import json -from pycricbuzz import Cricbuzz - - -class Matches: - """ - Class which gets and parses matches within 24 hours period - """ - - def __init__(self): - """ - cricket: class of cricbuzz - supported teams: supported teams by our module - flags: flags json with links - """ - - self.cricket = Cricbuzz() - - self.supported_teams = [ - "India", - "Australia", - "New Zealand", - "England", - "Bangladesh", - "South Africa", - "West Indies", - "Pakistan", - "Afghanistan", - "Sri Lanka", - ] - with open("app/fantasy_cricket/data/flags.json") as flag_file: - self.flags = json.load(flag_file) - - def get_match(self): - """ - Gets current matches dict - """ - matches = [] - for match in self.cricket.matches(): - - if ( - match["team1"]["name"] in self.supported_teams - and match["team2"]["name"] in self.supported_teams - #and match["mchstate"] == "preview" - ): - matches.append( - ( - match["team1"], - match["team2"], - self.flags["countries"][match["team1"]["name"]], - self.flags["countries"][match["team2"]["name"]], - ) - ) - return matches - - def get_squad_file_match_type(self, teams): - """ - Gets squad file based on teams - """ - - for match in self.cricket.matches(): - - if ( - match["team1"]["name"] == teams[0] - and match["team2"]["name"] == teams[1] - ): - match_det = ( - match["team1"]["squad_bench"] + match["team1"]["squad"], - match["team2"]["squad_bench"] + match["team2"]["squad"], - match["srs"] + match["mnum"], - match["type"] - ) - break - return match_det diff --git a/app/main.py b/app/main.py old mode 100644 new mode 100755 index 35e7fb4d..43efaab8 --- a/app/main.py +++ b/app/main.py @@ -16,14 +16,14 @@ along with this program. If not, see . """ -import os +from typing import List from fastapi.templating import Jinja2Templates from fastapi.responses import HTMLResponse, RedirectResponse, FileResponse -from fastapi import FastAPI, Form, Request, status +from fastapi import FastAPI, Form, Request, status, Query from fastapi.staticfiles import StaticFiles from fastapi.encoders import jsonable_encoder -from app.fantasy_cricket.team import Teams -from app.fantasy_cricket.utils import Matches +from app.fantasy_cricket.fantasy_leagues import Dream11 +from app.fantasy_cricket.matches import Matches # pylint: disable=missing-function-docstring # pylint: disable=global-variable-undefined @@ -41,18 +41,15 @@ @app.get("/", response_class=HTMLResponse) def home(request: Request): - matches = cricket.get_match() - teams = [ - [match[0]["name"], match[1]["name"], match[2]["flag"], match[3]["flag"]] - for match in matches - ] + matches = cricket.get_upcoming_match() return templates.TemplateResponse( - "index.html", {"request": request, "teams": teams} + "index.html", {"request": request, "teams": matches} ) @app.post("/") -async def home_post(match: str = Form(...)): +def home_post(match: str = Form(...)): + response = RedirectResponse( url="/playing11?team1=" + match.split(" vs ")[0] @@ -64,106 +61,56 @@ async def home_post(match: str = Form(...)): @app.get("/playing11", response_class=HTMLResponse) -def playing_11(request: Request, team1, team2): - - squad1, squad2, file, match_type = cricket.get_squad_file_match_type([team1, team2]) +def playing_11(request: Request, team1: str = Query(...), team2: str = Query(...)): + match_data = cricket.get_squad_match_type([team1, team2]) return templates.TemplateResponse( "Playing_11.html", { "request": request, - "squads": [squad1, squad2], - "file": file, - "match_type": match_type, + "squads": [match_data["team1_squad"], match_data["team2_squad"]], + "match_type": match_data["match_type"], "teams": [team1, team2], }, ) @app.post("/playing11") -async def playing_11_post(request: Request, file, match_type, team1, team2): - playings_11 = list(jsonable_encoder(await request.form()).keys()) - playings_11.remove("Confirm") - players1 = '"' + '","'.join(playings_11[0:11]) + '"' - players2 = '"' + '","'.join(playings_11[11:]) + '"' - - scrape_with_crochet( - file=file, - match_type=match_type, - teams=[team1, team2], - players2=players2, - players1=players1, - ) - return RedirectResponse( - url="/results?file=" + file, status_code=status.HTTP_302_FOUND +async def playing_11_post( + request: Request, + team1: str = Query(...), + team2: str = Query(...), + match_type: str = Query(...), +): + + play_11 = list(jsonable_encoder(await request.form()).keys()) + play_11.remove("Confirm") + url = ( + "/results/?match_type=" + match_type + "&team=" + team1 + "&team=" + team2 + "&" ) + for player in play_11[0:11]: + url += "player_team1=" + player + "&" + for player in play_11[11:]: + url += "player_team2=" + player + "&" + return RedirectResponse(url=url[:-1], status_code=status.HTTP_302_FOUND) @app.get("/results", response_class=HTMLResponse) -def result(request: Request, file): - t_d = Teams("app/fantasy_cricket/data/" + file + ".json") - captain, vcaptain = t_d.team() - team_list = t_d.player - players = [] - for i in team_list: - if i == captain: - tag_c = "(C)" - elif i == vcaptain: - tag_c = "(VC)" - else: - tag_c = "" - players.append(i + tag_c) - captain_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - vcaptain_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - for i, _ in enumerate(players): - if "(C)" in players[i]: - captain_list[i] = "(C)" - vcaptain_list[i] = "" - players[i] = players[i][:-3] - elif "(VC)" in players[i]: - vcaptain_list[i] = "(VC)" - captain_list[i] = "" - players[i] = players[i][:-4] - else: - vcaptain_list[i] = "" - captain_list[i] = "" +def result( + request: Request, + team: List[str] = Query(...), + match_type: str = Query(...), + player_team1: List[str] = Query(...), + player_team2: List[str] = Query(...), +): + t_d = Dream11(team[0], team[1]) + t_d.fetch_fantasy_team(player_team1, player_team2, match_type) + team = t_d.get_fantasy_team() return templates.TemplateResponse( "result.html", - context={ + { "request": request, - "c1": captain_list[0], - "v1": vcaptain_list[0], - "t1": players[0], - "c2": captain_list[1], - "v2": vcaptain_list[1], - "t2": players[1], - "c3": captain_list[2], - "v3": vcaptain_list[2], - "t3": players[2], - "c4": captain_list[3], - "v4": vcaptain_list[3], - "t4": players[3], - "c5": captain_list[4], - "v5": vcaptain_list[4], - "t5": players[4], - "c6": captain_list[5], - "v6": vcaptain_list[5], - "t6": players[5], - "c7": captain_list[6], - "v7": vcaptain_list[6], - "t7": players[6], - "c8": captain_list[7], - "v8": vcaptain_list[7], - "t8": players[7], - "c9": captain_list[8], - "v9": vcaptain_list[8], - "t9": players[8], - "c10": captain_list[9], - "v10": vcaptain_list[9], - "t10": players[9], - "c11": captain_list[10], - "v11": vcaptain_list[10], - "t11": players[10], + "team": team, }, ) @@ -171,23 +118,3 @@ def result(request: Request, file): @app.get("/robots.txt") def robots(): return FileResponse("app/robots.txt") - - -def scrape_with_crochet(file, match_type, teams, players1, players2): - - v_open = os.popen( - 'python3 -m scrapy crawl howstat -a match_type="' - + match_type - + '" -a team1="' - + teams[0] - + '" -a team2="' - + teams[1] - + '" -a players1=' - + players1 - + " -a players2=" - + players2 - + ' -a file="' - + file - + '" --loglevel DEBUG', - ) - v_open.close() diff --git a/app/robots.txt b/app/robots.txt old mode 100644 new mode 100755 index ac25b5fa..f7afb9da --- a/app/robots.txt +++ b/app/robots.txt @@ -2,5 +2,5 @@ User-agent: * Allow: / -Disallow: /playing11 -Disallow: /results \ No newline at end of file +Allow: /playing11 +Allow: /results \ No newline at end of file diff --git a/crawler/__init__.py b/crawler/__init__.py deleted file mode 100644 index 13e9dda5..00000000 --- a/crawler/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -""" - Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" -import sys - -sys.path.insert(1, "..") diff --git a/crawler/fantasy_leagues.py b/crawler/fantasy_leagues.py deleted file mode 100644 index 2d985548..00000000 --- a/crawler/fantasy_leagues.py +++ /dev/null @@ -1,88 +0,0 @@ - -""" -This modules defines all the fantasy leagues that -the project supports - Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" - - -class Scorer: - """ - Scorer class to use different formats of the game - - :py:class `t20`: A T20 dictionary which contains scoring metrics - :py:class `odi`: A ODI dictionary which contains scoring metrics - :py:class `test`: A TEST dictionary which contains scoring metrics - - :param scoring_dict: A dictionary containing scoring metrics. - Each value represents a list where index 0 represents `T20`, - 1 represents `ODI`, 2 represents `TEST` - """ - - def __init__(self, scoring_dict): - - self.t20 = {key: scoring_dict[key][0] for key in scoring_dict} - self.odi = {key: scoring_dict[key][1] for key in scoring_dict} - self.test = {key: scoring_dict[key][2] for key in scoring_dict} - - def get_score(self, stats, playing_format): - """ - Function which sums the score for that platform - depending on the format of the game - - :param playing_format: One of `ODI`,`T20`,`TEST` - """ - type_map = {"ODI": self.odi, "T20": self.t20, "TEST": self.test} - return sum(type_map[playing_format][key] * stats[key] for key in stats) - - -class Dream11: - """Dream11 League - - Supported platforms: - * ODI - * T20 - * TEST - """ - - name = "Dream11" - - batting_dict = Scorer( - { - "runs": [1, 1, 1], - "boundaries": [1, 1, 1], - "sixes": [2, 2, 2], - "50": [8, 4, 4], - "100": [16, 8, 8], - "duck": [-2, -3, -4], - } - ) - - bowling_dict = Scorer( - { - "wicket": [25, 25, 16], - "4-wicket-haul": [8, 4, 4], - "5-wicket-haul": [16, 8, 8], - "Maiden": [4, 8, 0], - } - ) - - wk_dict = Scorer( - { - "Catch": [8, 8, 8], - "Stump": [12, 12, 12], - } - ) diff --git a/crawler/items.py b/crawler/items.py deleted file mode 100644 index d7374db5..00000000 --- a/crawler/items.py +++ /dev/null @@ -1,41 +0,0 @@ -# Define here the models for your scraped items -# -# See documentation in: -# https://docs.scrapy.org/en/latest/topics/items.html - -""" - Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" - - -import scrapy - - -class PlayerItem(scrapy.Item): - """ - Class scrapes name of the player - Role of the player - Score he scored in that game - Team he is playing for - Date of the match - File it has been stored in - """ - name = scrapy.Field() - role = scrapy.Field() - score = scrapy.Field() - team = scrapy.Field() - date = scrapy.Field() - file = scrapy.Field() diff --git a/crawler/pipelines.py b/crawler/pipelines.py deleted file mode 100644 index a210acb1..00000000 --- a/crawler/pipelines.py +++ /dev/null @@ -1,57 +0,0 @@ -# Define your item pipelines here -# -# Don't forget to add your pipeline to the ITEM_PIPELINES setting -# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html - -""" - Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" -# useful for handling different item types with a single interface -import json -from statistics import mode -from itemadapter import ItemAdapter - - -class CricketcrawlerPipeline: - """ - Processes output given by the crawler and stores it in a json file - """ - def open_spider(self, spider): - """ - Works when spider starts crawling - """ - self.items = {} - self.name = None - - def close_spider(self, spider): - """ - Works when the spider stops crawling - """ - for item in self.items: - assert len(self.items[item]["role"]) == 5, f"{item}" - self.items[item]["role"] = mode(self.items[item]["role"]) - with open(self.name, "w") as f_p: - json.dump(self.items, f_p, indent=6) - - def process_item(self, item, spider): - """ - Processes files as the items are yielded - """ - self.name = f'app/fantasy_cricket/data/{item["file"]}.json' - if item["name"] not in self.items: - self.items[item["name"]] = {"role": [], "team": item["team"], "scores": {}} - self.items[item["name"]]["role"].append(item["role"]) - self.items[item["name"]]["scores"][item["date"]] = item["score"] diff --git a/crawler/spiders/__init__.py b/crawler/spiders/__init__.py deleted file mode 100644 index be112f24..00000000 --- a/crawler/spiders/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# This package will contain the spiders of your Scrapy project -# -# Please refer to the documentation for information on how to create and manage -# your spiders. -""" - Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" - -import sys - -sys.path.insert(1, "..") diff --git a/crawler/spiders/howstat.py b/crawler/spiders/howstat.py deleted file mode 100644 index 4d1b0c5c..00000000 --- a/crawler/spiders/howstat.py +++ /dev/null @@ -1,305 +0,0 @@ -""" - Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" -from scrapy import Request, Spider -from crawler.fantasy_leagues import Dream11 -from crawler.items import PlayerItem - - -class HowstatSpider(Spider): - """ - Spider which crawls through howstat.com - """ - name = "howstat" - allowed_domains = ["howstat.com"] - - start_urls = ["http://www.howstat.com/cricket/Statistics/Players/PlayerListCurrent.asp"] - def __init__(self, players1="", players2="", match_type="", *args, **kwargs): - super(HowstatSpider, self).__init__(*args, **kwargs) - self.names = { - "last": {self.team1: [], self.team2: []}, - "first": {self.team1: [], self.team2: []}, - } - for player in players1.split(","): - player_id = player.split() - if ( - player_id[0].lower().strip() == "de" - or player_id[0].lower().strip() == "du" - or player_id[0].lower().strip() == "van" - ): - player_id = [' '.join(player_id)] - if len(player_id) == 1: - self.names["last"][self.team1].append(player_id[0].lower().strip()) - self.names["first"][self.team1].append("") - elif len(player_id) >= 1: - self.names["last"][self.team1].append(player_id[-1].lower().strip()) - self.names["first"][self.team1].append(player_id[0].lower().strip()) - for player in players2.split(","): - player_id = player.split() - if len(player_id) == 1: - self.names["last"][self.team2].append(player_id[0].lower().strip()) - self.names["first"][self.team2].append("") - elif len(player_id) >= 1: - self.names["last"][self.team2].append(player_id[-1].lower().strip()) - self.names["first"][self.team2].append(player_id[0].lower().strip()) - assert ( - len(self.names["last"][self.team1]) - == 11 - == len(self.names["first"][self.team1]) - == len(self.names["first"][self.team2]) - == len(self.names["last"][self.team2]) - ) - if match_type == "TEST": - self.match_type = "" - else: - self.match_type = "_" + match_type - self.matches = {} - self.count = 0 - - def parse(self, response): - """ - Returns a request for the player found in the parsed url - """ - comma_map = { - "India": 1, - "Australia": 1, - "New Zealand": 1, - "England": 1, - "Bangladesh": 0, - "South Africa": 1, - "West Indies": 1, - "Pakistan": 0, - "Afghanistan": 0, - "Sri Lanka": 1, - } - teams = { - self.team1: response.selector.xpath( - "//table[@class='TableLined']/tr[td[3]/text()[contains(.,'" - + self.team1 - + "')]]/td[1]" - ), - self.team2: response.selector.xpath( - "//table[@class='TableLined']/tr[td[3]/text()[contains(.,'" - + self.team2 - + "')]]/td[1]" - ), - } - players = {} - for team in teams: - players[team] = [] - for player in teams[team]: - player_id = player.xpath("a/text()").get() - if comma_map[team]: - player_id = player_id.split(",") - try: - i = self.names["last"][team].index(player_id[0].lower().strip()) - if self.names["first"][team][i] == "": - players[team].append(player.xpath("a")) - else: - for initial in player_id[1].split(): - if ( - self.names["first"][team][i][0].lower() - == initial.lower().strip() - ): - players[team].append(player.xpath("a")) - break - except ValueError: - pass - else: - player_id = (player_id.split()[0], player_id.split()[-1]) - try: - i = self.names["last"][team].index(player_id[1].lower().strip()) - if self.names["first"][team][i] == "": - players[team].append(player.xpath("a")) - else: - if player_id[0].lower().strip() in self.names["first"][team]: - players[team].append(player.xpath("a")) - except ValueError: - pass - for team in players: - for player in players[team]: - player_id = player.xpath("@href").get().split("?")[1] - yield Request( - "http://www.howstat.com/cricket/Statistics/Players/PlayerProgressSummary" - + self.match_type - + ".asp?" - + player_id, - callback=self.parse_last5, - cb_kwargs={"team": team}, - ) - - def parse_last5(self, response, team): - """ - Gets the data of last 5 games for each player - """ - name = ( - response.xpath("//td[contains(@class, 'Banner')]/text()") - .get() - .strip() - .split("(")[0] - .strip() - ) - if self.match_type != "": - matches = response.xpath( - '//table[@class = "TableLined"]/tr[position()>last()-5]/td[2]/a' - ) - else: - matches = response.xpath( - '//table[@class = "TableLined"]/tr[position()>last()-10]/td[2]/a' - ) - crawl = False - if len(matches) >= 5: - crawl = True - matches = matches[len(matches)-5:] - for match in matches: - if crawl: - yield Request( - "http://www.howstat.com/cricket/Statistics" - + match.xpath("@href").get().split("..")[1], - callback=self.parse_scorecard, - cb_kwargs={ - "name": name, - "pid" : response.url.split("?")[-1], - "team" : team, - "date" : match.get().split('>')[1].split('<')[0] - }, - dont_filter=True, - ) - - def parse_scorecard(self, response, name, pid, team, date): - """ - parses the Scorecard - """ - scorelisxpath = ( - "//table/tr/td/table/tr/td/table/tr[td[a[@href[contains(.,'" - + pid - + "')]]]]" - ) - lis = response.selector.xpath(scorelisxpath) - batting_dict = {} - bowling_dict = {} - keeping_dict = {} - role = None - for score in lis: - if score.xpath("td[1]/text()[contains(.,'†')]").get(): - role = "wk" - elif score.xpath("td[2]/text()[contains(.,'.')]"): - if "Maiden" not in bowling_dict: - bowling_dict["Maiden"] = 0 - bowling_dict["Maiden"] += int(score.xpath("td[3]/text()").get().strip()) - if "wicket" not in bowling_dict: - bowling_dict["wicket"] = 0 - bowling_dict["wicket"] += int(score.xpath("td[5]/text()").get().strip()) - if "4-wicket-haul" not in bowling_dict: - bowling_dict["4-wicket-haul"] = 0 - if "5-wicket-haul" not in bowling_dict: - bowling_dict["5-wicket-haul"] = 0 - if int(score.xpath("td[5]/text()").get().strip()) >= 5: - bowling_dict["5-wicket-haul"] += 1 - elif int(score.xpath("td[5]/text()").get().strip()) >= 4: - bowling_dict["4-wicket-haul"] += 1 - else: - current = score - for _ in range(0, 4): - if role == "wk": - break - current = current.xpath("following-sibling::tr") - if current.xpath("td[1][contains(.,'Extras')]"): - role = "bowl" - break - if "runs" not in batting_dict: - batting_dict["runs"] = 0 - if score.xpath("td[3]/text()").get().strip() == "": - batting_dict["runs"] = 0 - batting_dict["boundaries"] = 0 - batting_dict["sixes"] = 0 - batting_dict["50"] = 0 - batting_dict["100"] = 0 - batting_dict["duck"] = 0 - continue - batting_dict["runs"] += int(score.xpath("td[3]/text()").get().strip()) - if "boundaries" not in batting_dict: - batting_dict["boundaries"] = 0 - batting_dict["boundaries"] += int( - score.xpath("td[5]/text()").get().strip() - ) - if "sixes" not in batting_dict: - batting_dict["sixes"] = 0 - batting_dict["sixes"] += int(score.xpath("td[6]/text()").get().strip()) - if "50" not in batting_dict: - batting_dict["50"] = 0 - if "100" not in batting_dict: - batting_dict["100"] = 0 - if "duck" not in batting_dict: - batting_dict["duck"] = 0 - if int(score.xpath("td[3]/text()").get().strip()) >= 100: - batting_dict["100"] += 1 - elif int(score.xpath("td[3]/text()").get().strip()) >= 50: - batting_dict["50"] += 1 - elif ( - int(score.xpath("td[3]/text()").get().strip()) == 50 - and role != "bowl" - ): - batting_dict["duck"] += 1 - if role == "wk": - wk_score = score.xpath( - "//table/tr/td/table/tr/td/table/tr[td[2][@valign and @width]/text()[contains(.,'" - + name.split()[-1].strip() - + "')]]" - ).xpath("td[2]/text()") - if wk_score: - if "Catch" not in keeping_dict: - keeping_dict["Catch"] = 0 - if "Stump" not in keeping_dict: - keeping_dict["Stump"] = 0 - for wk_s in wk_score: - if "c †" in wk_s.get(): - keeping_dict["Catch"] += 1 - elif "st †" in wk_s.get(): - keeping_dict["Stump"] += 1 - - if bowling_dict != {} and role != "bowl": - role = "all" - elif bowling_dict == {} and role != "wk": - role = "bat" - score = 0 - match_type = {"_ODI": "ODI", "_T20": "T20", "": "TEST"} - if role == "wk": - score += Dream11.wk_dict.get_score( - keeping_dict, match_type[self.match_type] - ) - score += Dream11.batting_dict.get_score( - batting_dict, match_type[self.match_type] - ) - elif role == "bowl": - score += Dream11.bowling_dict.get_score( - bowling_dict, match_type[self.match_type] - ) - elif role == "all": - score += Dream11.bowling_dict.get_score( - bowling_dict, match_type[self.match_type] - ) - score += Dream11.batting_dict.get_score( - batting_dict, match_type[self.match_type] - ) - elif role == "bat": - score += Dream11.batting_dict.get_score( - batting_dict, match_type[self.match_type] - ) - - yield PlayerItem( - name=name, score=score, role=role, date=date, team=team, file=self.file - ) diff --git a/Dockerfile b/docker/11tastic/Dockerfile old mode 100644 new mode 100755 similarity index 63% rename from Dockerfile rename to docker/11tastic/Dockerfile index f1cf0de6..c12ff3d0 --- a/Dockerfile +++ b/docker/11tastic/Dockerfile @@ -6,10 +6,4 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* COPY ./requirements.txt /app/requirements.txt RUN pip install --no-cache-dir --user -r /app/requirements.txt; rm -r /tmp - -FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8-slim -COPY --from=compile-image /root/.local /root/.local COPY ./app /app/app -COPY ./crawler /app/crawler -COPY ./scrapy.cfg /app/scrapy.cfg -ENV PYTHONPATH=/app \ No newline at end of file diff --git a/docker/docker-compose-test.yaml b/docker/docker-compose-test.yaml new file mode 100644 index 00000000..8c092e60 --- /dev/null +++ b/docker/docker-compose-test.yaml @@ -0,0 +1,17 @@ +version: '3.3' +services: + espncricinfo: + build: + context: .. + dockerfile: docker/espncricinfo/Dockerfile + ports: + - "9080:9080" + environment: + - LOG_LEVEL=DEBUG + test: + build: + context: .. + dockerfile: docker/test/Dockerfile + depends_on: + - espncricinfo + \ No newline at end of file diff --git a/docker-compose.yaml b/docker/docker-compose.yaml old mode 100644 new mode 100755 similarity index 50% rename from docker-compose.yaml rename to docker/docker-compose.yaml index 3ffde91a..04d3749f --- a/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -1,14 +1,22 @@ version: '3.3' services: + espncricinfo: + image: espncricinfo:latest + ports: + - "9080:9080" + environment: + - LOG_LEVEL=DEBUG app: - image: best11fantasycricket:latest + image: best11:latest + depends_on: + - espncricinfo ports: - "8080:80" environment: - MAX_WORKERS=1 - - LOG_LEVEL=DEBUG deploy: resources: limits: cpus: '1' - memory: 100M \ No newline at end of file + memory: 100M + \ No newline at end of file diff --git a/docker/espncricinfo/Dockerfile b/docker/espncricinfo/Dockerfile new file mode 100644 index 00000000..d1116f37 --- /dev/null +++ b/docker/espncricinfo/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3 + +WORKDIR /crawler + +RUN pip install --no-cache-dir scrapyrt; rm -r /tmp + +COPY ./espncricinfo /crawler/espncricinfo +COPY ./scrapy.cfg /crawler/scrapy.cfg + +EXPOSE 9080 +CMD ["scrapyrt", "-i", "0.0.0.0"] \ No newline at end of file diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile new file mode 100644 index 00000000..e9a86b0d --- /dev/null +++ b/docker/test/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3 + +WORKDIR /test + +COPY ./requirements.txt /test/requirements.txt +RUN pip install --no-cache-dir -r requirements.txt; rm -r /tmp +RUN pip install --no-cache-dir pytest; + +COPY ./test /test/test +COPY ./app /test/app + +CMD ["pytest"] + diff --git a/espncricinfo/__init__.py b/espncricinfo/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/espncricinfo/items.py b/espncricinfo/items.py new file mode 100755 index 00000000..07a8cb46 --- /dev/null +++ b/espncricinfo/items.py @@ -0,0 +1,37 @@ +# Define here the models for your scraped items +# +# See documentation in: +# https://docs.scrapy.org/en/latest/topics/items.html + +import scrapy + + +class PlayerItem(scrapy.Item): + + name = scrapy.Field() + image = scrapy.Field() + role = scrapy.Field() + player_id = scrapy.Field() + + +class ScoreItem(scrapy.Item): + + runs = scrapy.Field() + boundaries = scrapy.Field() + sixes = scrapy.Field() + wicket = scrapy.Field() + Maiden = scrapy.Field() + Catch = scrapy.Field() + Stump = scrapy.Field() + match_id = scrapy.Field() + + +class LiveMatchItem(scrapy.Item): + + team1 = scrapy.Field() + team2 = scrapy.Field() + match_id = scrapy.Field() + team1_squad = scrapy.Field() + team2_squad = scrapy.Field() + team1_id = scrapy.Field() + team2_id = scrapy.Field() diff --git a/crawler/settings.py b/espncricinfo/settings.py old mode 100644 new mode 100755 similarity index 61% rename from crawler/settings.py rename to espncricinfo/settings.py index 4a6493f8..467298b2 --- a/crawler/settings.py +++ b/espncricinfo/settings.py @@ -1,20 +1,4 @@ -""" - Copyright (C) 2020 Royston E Tauro & Sammith S Bharadwaj & Shreyas Raviprasad - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -""" -# Scrapy settings for cricketcrawler project +# Scrapy settings for espncricinfo project # # For simplicity, this file contains only settings considered important or # commonly used. You can find more settings consulting the documentation: @@ -23,16 +7,18 @@ # https://docs.scrapy.org/en/latest/topics/downloader-middleware.html # https://docs.scrapy.org/en/latest/topics/spider-middleware.html -BOT_NAME = "crawler" +import os -SPIDER_MODULES = ["crawler.spiders"] -NEWSPIDER_MODULE = "crawler.spiders" +BOT_NAME = "espncricinfo" + +SPIDER_MODULES = ["espncricinfo.spiders"] +NEWSPIDER_MODULE = "espncricinfo.spiders" # Crawl responsibly by identifying yourself (and your website) on the user-agent -# USER_AGENT = 'cricketcrawler (+http://www.yourdomain.com)' +# USER_AGENT = 'espncricinfo (+http://www.yourdomain.com)' -# Obey robots.txt rules- +# Obey robots.txt rules ROBOTSTXT_OBEY = True # Configure maximum concurrent requests performed by Scrapy (default: 16) @@ -41,7 +27,7 @@ # Configure a delay for requests for the same website (default: 0) # See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay # See also autothrottle settings and docs -DOWNLOAD_DELAY = 0.5 +# DOWNLOAD_DELAY = 3 # The download delay setting will honor only one of: # CONCURRENT_REQUESTS_PER_DOMAIN = 16 # CONCURRENT_REQUESTS_PER_IP = 16 @@ -60,14 +46,17 @@ # Enable or disable spider middlewares # See https://docs.scrapy.org/en/latest/topics/spider-middleware.html -# SPIDER_MIDDLEWARES = { -# 'cricketcrawler.middlewares.CricketcrawlerSpiderMiddleware': 543, -# } - # Enable or disable downloader middlewares +if os.getenv("TEST"): + SPIDER_MIDDLEWARES = { + 'scrapy_autounit.AutounitMiddleware': 950 + } + AUTOUNIT_ENABLED = True + + # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html # DOWNLOADER_MIDDLEWARES = { -# 'cricketcrawler.middlewares.CricketcrawlerDownloaderMiddleware': 543, +# 'espncricinfo.middlewares.EspncricinfoDownloaderMiddleware': 543, # } # Enable or disable extensions @@ -78,29 +67,27 @@ # Configure item pipelines # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html -ITEM_PIPELINES = { - "crawler.pipelines.CricketcrawlerPipeline": 300, -} +# ITEM_PIPELINES = { +# 'espncricinfo.pipelines.EspncricinfoPipeline': 300, +# } # Enable and configure the AutoThrottle extension (disabled by default) # See https://docs.scrapy.org/en/latest/topics/autothrottle.html -AUTOTHROTTLE_ENABLED = True +# AUTOTHROTTLE_ENABLED = True # The initial download delay -AUTOTHROTTLE_START_DELAY = 1 +# AUTOTHROTTLE_START_DELAY = 5 # The maximum download delay to be set in case of high latencies -AUTOTHROTTLE_MAX_DELAY = 60 +# AUTOTHROTTLE_MAX_DELAY = 60 # The average number of requests Scrapy should be sending in parallel to -# each remote serverHTTP status code is not handled or not allowed -AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0 +# each remote server +# AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0 # Enable showing throttling stats for every response received: -AUTOTHROTTLE_DEBUG = True +# AUTOTHROTTLE_DEBUG = False # Enable and configure HTTP caching (disabled by default) # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings HTTPCACHE_ENABLED = True HTTPCACHE_EXPIRATION_SECS = 0 HTTPCACHE_DIR = "httpcache" -HTTPCACHE_IGNORE_HTTP_CODES = [500] +HTTPCACHE_IGNORE_HTTP_CODES = [] HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage" - -RETRY_HTTP_CODES = [502, 503, 504, 400, 408] diff --git a/espncricinfo/spiders/__init__.py b/espncricinfo/spiders/__init__.py new file mode 100755 index 00000000..ebd689ac --- /dev/null +++ b/espncricinfo/spiders/__init__.py @@ -0,0 +1,4 @@ +# This package will contain the spiders of your Scrapy project +# +# Please refer to the documentation for information on how to create and manage +# your spiders. diff --git a/espncricinfo/spiders/match-spider.py b/espncricinfo/spiders/match-spider.py new file mode 100755 index 00000000..39ba3e60 --- /dev/null +++ b/espncricinfo/spiders/match-spider.py @@ -0,0 +1,99 @@ +import scrapy +from espncricinfo.items import ScoreItem + + +class MatchesSpider(scrapy.Spider): + + name = "espn-matches" + allowed_domains = ["espncricinfo.com"] + + def start_requests(self): + return [ + scrapy.Request( + url="https://stats.espncricinfo.com/ci/engine/player/253802.html?class=1&orderby=start&orderbyad=reverse&template=results&type=allround&view=match", + callback=self.parse, + ) + ] + + def parse(self, response): + + matches = response.selector.xpath( + "//a[@href[contains(. , '/ci/engine/match')] and @title]" + )[:5] + pid = response.url.split(".html")[0].split("player/")[1] + score_dict = { + "runs": None, + "boundaries": None, + "sixes": None, + "wicket": None, + "Maiden": None, + "Catch": None, + "Stump": None, + } + keeping_stats = response.selector.xpath( + "//table[caption[contains(.,'Match by match list')]]//tr[td]" + )[:5] + for i, match in enumerate(matches): + score_dict["Catch"] = score_dict["Stump"] = 0 + if "Test" in match.xpath("text()").get(): + score_dict["Catch"] += int( + keeping_stats[i].xpath("td[6]/text()").get().strip() + ) + score_dict["Catch"] += int( + keeping_stats[i].xpath("td[7]/text()").get().strip() + ) + elif ( + "ODI" in match.xpath("text()").get() + or "T20" in match.xpath("text()").get() + ): + score_dict["Catch"] += int( + keeping_stats[i].xpath("td[4]/text()").get().strip() + ) + score_dict["Catch"] += int( + keeping_stats[i].xpath("td[5]/text()").get().strip() + ) + yield scrapy.Request( + url="https://www.espncricinfo.com" + match.xpath("@href").get(), + callback=self.parse_match, + cb_kwargs={ + "pid": pid, + "score_dict": score_dict, + "match_id": match.xpath("text()").get(), + }, + ) + + def parse_match(self, response, pid, score_dict, match_id): + + batsman = response.selector.xpath( + "//table[@class='table batsman']//tr[td[a[@href[contains(., '" + + pid + + "')]]]]" + ) + if batsman: + score_dict["runs"] = score_dict["boundaries"] = score_dict["sixes"] = 0 + for bat in batsman: + score_dict["runs"] += int(bat.xpath("td[3]/text()").get().strip()) + score_dict["boundaries"] += int(bat.xpath("td[6]/text()").get().strip()) + score_dict["sixes"] += int(bat.xpath("td[7]/text()").get().strip()) + + bowler = response.selector.xpath( + "//table[@class='table bowler']//tr[td[a[@href[contains(., '" + + pid + + "')]]]]" + ) + if bowler: + score_dict["wicket"] = score_dict["Maiden"] = 0 + for bowl in bowler: + score_dict["wicket"] += int(bowl.xpath("td[3]/text()").get().strip()) + score_dict["Maiden"] += int(bowl.xpath("td[5]/text()").get().strip()) + + yield ScoreItem( + runs=score_dict["runs"], + boundaries=score_dict["boundaries"], + sixes=score_dict["sixes"], + wicket=score_dict["wicket"], + Maiden=score_dict["Maiden"], + Catch=score_dict["Catch"], + Stump=score_dict["Stump"], + match_id=match_id, + ) diff --git a/espncricinfo/spiders/player-spider.py b/espncricinfo/spiders/player-spider.py new file mode 100755 index 00000000..e4078800 --- /dev/null +++ b/espncricinfo/spiders/player-spider.py @@ -0,0 +1,32 @@ +import scrapy +from espncricinfo.items import PlayerItem + + +class PlayerListSpider(scrapy.Spider): + + name = "espn-players" + allowed_domains = ["espncricinfo.com"] + + def start_requests(self): + return [ + scrapy.Request( + url="https://www.espncricinfo.com/ci/content/player/253802.html", + callback=self.parse, + ) + ] + + def parse(self, response): + + name = response.selector.xpath("//div[@class='ciPlayernametxt']/div/h1/text()") + role = response.selector.xpath( + "//p[@class='ciPlayerinformationtxt' and b/text()[contains(.,'Playing role')]]/span/text()" + ) + image = response.selector.xpath( + "//img[@src[contains(.,'espncricinfo.com/inline/content')]]" + ).xpath("@src") + + yield PlayerItem( + name=name.get(), + role=role.get(), + image=image.get(), + ) diff --git a/espncricinfo/spiders/upcoming-spider.py b/espncricinfo/spiders/upcoming-spider.py new file mode 100755 index 00000000..304ceec5 --- /dev/null +++ b/espncricinfo/spiders/upcoming-spider.py @@ -0,0 +1,86 @@ +import scrapy +import datetime +import json +from espncricinfo.items import LiveMatchItem + + +class LiveSpider(scrapy.Spider): + + name = "espn-live" + allowed_domains = ["espncricinfo.com"] + + def start_requests(self): + return [ + scrapy.Request( + url="https://www.espncricinfo.com/live-cricket-match-schedule-fixtures", + callback=self.parse, + ) + ] + + def parse(self, response): + + matches = response.selector.xpath("//a[@class = 'match-info-link-FIXTURES']") + now = datetime.datetime.now().date() + + for match in matches: + + try: + if ( + datetime.datetime.strptime( + match.xpath("div/div/div/span/text()").get().split(",")[0], + "%d-%b-%Y", + ).date() + - now + ).days <= 7: + match_link = [ + match.xpath("@href").get() + for team_div in match.xpath( + "div/div/div[@class='teams']/div[@class='team']" + ) + if team_div.xpath("div/p/text()").get() + in [ + "India", + "Australia", + "England", + "Bangladesh", + "New Zealand", + "South Africa", + "Pakistan", + "Sri Lanka", + "West Indies", + "Afghanistan", + ] + ] + if match_link: + yield scrapy.Request( + url="https://www.espncricinfo.com/matches/engine/match/" + + match_link[0] + .split("/live-cricket-score")[0] + .split("-")[-1] + + ".json", + callback=self.parse_match, + ) + except ValueError: + pass + + def parse_match(self, response): + + match_data = json.loads(response.text) + team1_squad = [] + team2_squad = [] + for squad in match_data["team"][0]["squad"]: + team1_squad.append( + {"name": squad["card_long"], "player_id": squad["object_id"]} + ) + for squad in match_data["team"][1]["squad"]: + team2_squad.append({"name": squad["card_long"], "id": squad["object_id"]}) + + yield LiveMatchItem( + team1=match_data["match"]["team1_name"], + team2=match_data["match"]["team2_name"], + match_id=match_data["match"]["international_class_id"], + team1_squad=team1_squad, + team2_squad=team2_squad, + team1_id=match_data["match"]["team1_id"], + team2_id=match_data["match"]["team2_id"], + ) diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 index 5a3d3ab1..50eea6c6 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ sklearn==0.0 fastapi[all]==0.63.0 -pycricbuzz==2.4 scrapy==2.4 +scrapyrt==0.11.0 \ No newline at end of file diff --git a/scrapy.cfg b/scrapy.cfg old mode 100644 new mode 100755 index 83a4eef6..a5cd4ae5 --- a/scrapy.cfg +++ b/scrapy.cfg @@ -4,8 +4,9 @@ # https://scrapyd.readthedocs.io/en/latest/deploy.html [settings] -default = crawler.settings +default = espncricinfo.settings [deploy] #url = http://localhost:6800/ -project = crawler +project = espncricinfo + diff --git a/test/__init__.py b/test/__init__.py index 9102b2dc..e69de29b 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,7 +0,0 @@ -""" -Setting paths for testing -""" - -import sys - -sys.path.insert(1, "..") diff --git a/test/test_main.py b/test/test_main.py index 22ffacb3..970e1975 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -4,7 +4,7 @@ import os from fastapi.testclient import TestClient from app.main import app -from app.fantasy_cricket.utils import Matches +from app.fantasy_cricket.matches import Matches client = TestClient(app) cricket = Matches() @@ -16,21 +16,13 @@ def test_home(): assert response.status_code == 200 -def test_home_post(): - teams = [ - [match[0]["name"], match[1]["name"]] - for match in cricket.get_match() - ] - for team in teams: - response = client.post("/", data={"match": team[0]+" vs "+team[1]}) - assert response.status_code == 302 - def test_playing_11(): - teams = [ - [match[0]["name"], match[1]["name"]] - for match in cricket.get_match() - ] - for team in teams: - response = client.get("/playing11?team1="+team[0]+"&team2="+team[1], - data={"match": team[0]+" vs "+team[1]}) + + matches = cricket.get_upcoming_match() + for teams in matches: + response = client.get("/playing11?team1="+ teams["team1"] + "&team2=" + teams["team2"]) assert response.status_code == 200 + +def test_results(): + response = client.get("/results?match_type=1&team=India&team=England&player_team1=253802&player_team1=277916&player_team1=398438&player_team1=26421&player_team1=625383&player_team1=940973&player_team1=625371&player_team1=931581&player_team1=32540&player_team1=422108&player_team1=34102&player_team2=303669&player_team2=8917&player_team2=8608&player_team2=669855&player_team2=646847&player_team2=10617&player_team2=398778&player_team2=308967&player_team2=364788&player_team2=641423&player_team2=455524") + assert response.status_code == 200