├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── enhancement.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── issue-branch.yml ├── labeler.yml └── workflows │ ├── autoyapf.yml │ ├── codeql-analysis.yml │ ├── create-issue-branch.yml │ ├── django.yml │ └── label.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── codedigger ├── atcoder │ ├── __init__.py │ ├── admin.py │ ├── api.py │ ├── apps.py │ ├── cron.py │ ├── migrations │ │ └── __init__.py │ ├── model_utils.py │ ├── models.py │ ├── scrapers.py │ ├── scrapers_utils.py │ ├── serializers.py │ ├── tests │ │ ├── test_api.py │ │ ├── test_scrapers.py │ │ ├── test_setup.py │ │ └── test_upsolve.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── blog │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_blog_youtube_link.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── codechef │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── cron.py │ ├── fixtures │ │ ├── cc_contest_problem.json │ │ └── cc_contests.json │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20211227_1026.py │ │ └── __init__.py │ ├── model_utils.py │ ├── models.py │ ├── scraper.py │ ├── scraper_utils.py │ ├── serializers.py │ ├── test_fixtures │ │ ├── model_utils_fixture.py │ │ └── scraper_utils_fixture.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_model_utils.py │ │ ├── test_scraper.py │ │ ├── test_setup.py │ │ ├── test_upsolve.py │ │ └── test_views.py │ ├── urls.py │ └── views.py ├── codedigger │ ├── .env.example │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── codeforces │ ├── __init__.py │ ├── admin.py │ ├── api.py │ ├── api_utils.py │ ├── apps.py │ ├── codeforcesProblemSet.py │ ├── contestProblem.py │ ├── cron.py │ ├── email │ │ └── rating_reminder.py │ ├── fixtures │ │ ├── cf_contest.json │ │ └── cf_users.json │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20201223_1545.py │ │ ├── 0003_auto_20201224_0950.py │ │ ├── 0004_auto_20201224_1007.py │ │ ├── 0005_contest_isupdated.py │ │ ├── 0006_auto_20210106_1021.py │ │ ├── 0007_auto_20210106_1047.py │ │ ├── 0008_auto_20210129_2145.py │ │ ├── 0009_auto_20210929_1435.py │ │ ├── 0010_codeforcesproblemset.py │ │ └── __init__.py │ ├── models.py │ ├── models_utils.py │ ├── scraper.py │ ├── scraper_utils.py │ ├── serializers.py │ ├── test_fixtures │ │ ├── models_utils_fixture.py │ │ └── rating_change_fixture.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_api.py │ │ ├── test_models_utils.py │ │ ├── test_problems.py │ │ ├── test_scraper_utils.py │ │ ├── test_setup.py │ │ ├── test_upsolve.py │ │ └── test_views.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── contest │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── contestMaker.py │ ├── cron.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── model_utils.py │ ├── models.py │ ├── resultMaker.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── lists │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── cron.py │ ├── fixtures │ │ ├── enrolled.json │ │ ├── list_info.json │ │ ├── lists.json │ │ ├── problems.json │ │ ├── profiles.json │ │ ├── solved.json │ │ ├── user.json │ │ └── userfriends.json │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20210102_1525.py │ │ ├── 0003_list_public.py │ │ ├── 0004_listextrainfo.py │ │ ├── 0005_ladderstarted.py │ │ ├── 0006_enrolled.py │ │ ├── 0007_auto_20211227_1032.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── serializers.py │ ├── solved_update.py │ ├── test_fixtures │ │ └── profile_fixtures.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_lists.py │ │ ├── test_model.py │ │ ├── test_setup.py │ │ └── test_views.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── manage.py ├── problem │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── cron.py │ ├── fixtures │ │ ├── at_problems.json │ │ ├── atcoder_contests.json │ │ ├── cc_problems.json │ │ ├── cf_problems.json │ │ └── cf_solved.json │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20210106_1154.py │ │ ├── 0003_auto_20210106_1158.py │ │ ├── 0004_auto_20210106_1200.py │ │ ├── 0005_auto_20210129_2145.py │ │ └── __init__.py │ ├── models.py │ ├── scraper │ │ ├── atcoder.py │ │ ├── autocodechef.py │ │ ├── codechef.py │ │ ├── spoj.py │ │ ├── taglist.txt │ │ └── uva.py │ ├── sender.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_problem_filter.py │ │ ├── test_setup.py │ │ └── test_upsolve.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── social_auth │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── google.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── register.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── templates │ ├── codeforces │ │ ├── footer.html │ │ └── rating_reminder.html │ └── user │ │ └── send_mail.html ├── user │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── exception.py │ ├── handle_validator.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20201228_1110.py │ │ ├── 0003_auto_20201229_2120.py │ │ ├── 0003_profile_gurus.py │ │ ├── 0004_merge_20210104_1504.py │ │ ├── 0005_auto_20210112_1207.py │ │ ├── 0006_auto_20210112_1220.py │ │ └── __init__.py │ ├── models.py │ ├── param_validators.py │ ├── permissions.py │ ├── profile.py │ ├── response.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_model.py │ │ ├── test_setup.py │ │ └── test_views.py │ ├── urls.py │ ├── utils.py │ ├── validator_functions.py │ └── views.py ├── utils │ ├── common.py │ └── email.py └── uva │ ├── __init__.py │ ├── admin.py │ ├── api.py │ ├── apps.py │ ├── cron.py │ ├── migrations │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── docs ├── CNAME ├── _config.yml ├── format.md ├── index.html └── routes │ ├── atcoder │ └── atcoderUpsolve.html │ ├── codechef │ └── codechefUpsolve.html │ ├── codeforces │ ├── codeforcesSearchUser.html │ └── codeforcesUpsolve.html │ ├── doc.html │ ├── lists │ ├── EnrollIntoList.html │ ├── SearchUserlistView.html │ ├── UserlistAddProblemView.html │ ├── UserlistStats.html │ └── ViewAllEnrolledList.html │ ├── problem │ └── problemFilter.html │ ├── sample │ └── sample.html │ └── static │ ├── css │ ├── doc.css │ ├── global.css │ └── landing.css │ ├── images │ └── codedigger-logo-48px.png │ └── js │ └── docs.js ├── mysqlclient-1.4.6-cp39-cp39-win_amd64.whl └── requirements.txt /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: 'Issue Type: Bug Report' 5 | 6 | --- 7 | 8 | **Describe the bug** 9 | A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** 12 | Provide the url/ api endpoint with parameters provided where you see the bug or the complete steps to reproduce the bug again. 13 | 14 | **Expected behavior** 15 | A clear and concise description of what you expected to happen. 16 | 17 | **Actual behavior** 18 | A clear and concise description of what happens or what is the error you got? 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement 3 | about: Do you want to improve an existing feature? 4 | labels: "Issue Type: Enhancement" 5 | --- 6 | 7 | **Is your enhancement request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Describe all the endpoints required for your enhancement** 17 | A clear and concise description of all the expected routes for this feature. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Do you need a new feature? 4 | labels: 'Issue Type: Feature Request' 5 | 6 | --- 7 | 8 | **Is your feature request related to a problem? Please describe.** 9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 10 | 11 | **Describe the solution you'd like** 12 | A clear and concise description of what you want to happen. 13 | 14 | **Describe alternatives you've considered** 15 | A clear and concise description of any alternative solutions or features you've considered. 16 | 17 | **Describe all the endpoints required for this feature** 18 | A clear and concise description of all the expected routes for this feature. 19 | 20 | **Additional context** 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request Template 2 | 3 | ## Description 4 | 5 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 6 | Fixes # (issue) 7 | 8 | **List any dependencies that are required for this change** 9 | If you have installed any extra python library, do mention here. 10 | 11 | ## Type of change 12 | 13 | Please delete options that are not relevant. 14 | 15 | - [ ] Bug fix (non-breaking change which fixes an issue) 16 | - [ ] New feature (non-breaking change which adds functionality) 17 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 18 | - [ ] This change requires a documentation update 19 | 20 | ## Provide all the new routes 21 | 22 | Please write all the new routes/ endpoints you have formed and describe what the purpose of them with proper documentation and example. 23 | 24 | ## How Has This Been Tested? 25 | 26 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. 27 | Please also list any relevant details for your test configuration. 28 | 29 | ## Checklist: 30 | 31 | - [ ] I have performed a self-review of my own code 32 | - [ ] I have commented my code, particularly in hard-to-understand areas 33 | - [ ] My changes generate no new warnings 34 | - [ ] I have checked my code and corrected any misspellings 35 | -------------------------------------------------------------------------------- /.github/issue-branch.yml: -------------------------------------------------------------------------------- 1 | mode: chatops 2 | gitSafeReplacementChar: '-' 3 | defaultBranch: 'master' 4 | autoCloseIssue: true 5 | branchName: 'issue-${issue.number}/${issue.title,}' 6 | branches: 7 | - label: enhancement 8 | prefix: feature/${sender.login}/ 9 | - label: bug 10 | prefix: bugfix/${sender.login}/ 11 | - label: refactor 12 | prefix: refactor/${sender.login}/ 13 | - label: documentation 14 | prefix: docs/${sender.login}/ 15 | - label: '*' 16 | prefix: issues/${sender.login}/ 17 | experimental: 18 | branchNameArgument: true 19 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | root: 2 | - codedigger/codedigger/**/* 3 | 4 | -------------------------------------------------------------------------------- /.github/workflows/autoyapf.yml: -------------------------------------------------------------------------------- 1 | name: autoyapf 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | paths: 8 | - '**.py' 9 | 10 | jobs: 11 | label: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | max-parallel: 4 15 | matrix: 16 | python-version: [3.8.10] 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | ref: ${{github.head_ref}} 21 | fetch-depth: 0 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Installing PEP8 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install yapf 30 | yapf -i -r -vv -p codedigger/ 31 | # URL="https://api.github.com/repos/${{github.repository}}/pulls/${{ github.event.pull_request.number }}/files?per_page=100" 32 | # FILES=$(curl -s -X GET -G $URL | jq -r '.[] | .filename') 33 | # echo $FILES 34 | - name: Check for modified files 35 | id: git-check 36 | run: | 37 | echo ::set-output name=modified::$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi) 38 | - name: Git Push Changes 39 | if: steps.git-check.outputs.modified == 'true' 40 | run: | 41 | git config user.name github-actions 42 | git config user.email github-actions@github.com 43 | git add . 44 | git commit -m "refactor: automatic code reformatting to PEP8 by yapf" 45 | git push 46 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | schedule: 16 | - cron: '0 0 * * 0' 17 | 18 | jobs: 19 | analyze: 20 | name: Analyze 21 | runs-on: ubuntu-latest 22 | permissions: 23 | actions: read 24 | contents: read 25 | security-events: write 26 | 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | language: [ 'python' ] 31 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 32 | # Learn more: 33 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 34 | 35 | steps: 36 | - name: Checkout repository 37 | uses: actions/checkout@v2 38 | 39 | # Initializes the CodeQL tools for scanning. 40 | - name: Initialize CodeQL 41 | uses: github/codeql-action/init@v1 42 | with: 43 | languages: ${{ matrix.language }} 44 | # If you wish to specify custom queries, you can do so here or in a config file. 45 | # By default, queries listed here will override any specified in a config file. 46 | # Prefix the list here with "+" to use these queries and those in the config file. 47 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 48 | 49 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 50 | # If this step fails, then you should remove it and run the build manually (see below) 51 | - name: Autobuild 52 | uses: github/codeql-action/autobuild@v1 53 | 54 | # ℹ️ Command-line programs to run using the OS shell. 55 | # 📚 https://git.io/JvXDl 56 | 57 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 58 | # and modify them (or add more) to build your code if your project 59 | # uses a compiled language 60 | 61 | #- run: | 62 | # make bootstrap 63 | # make release 64 | 65 | - name: Perform CodeQL Analysis 66 | uses: github/codeql-action/analyze@v1 67 | -------------------------------------------------------------------------------- /.github/workflows/create-issue-branch.yml: -------------------------------------------------------------------------------- 1 | name: Create Issue Branch 2 | 3 | on: 4 | issues: 5 | types: [ assigned ] 6 | issue_comment: 7 | types: [ created ] 8 | pull_request: 9 | types: [ closed ] 10 | 11 | jobs: 12 | create_issue_branch_job: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Create Issue Branch 16 | uses: robvanderleek/create-issue-branch@main 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/django.yml: -------------------------------------------------------------------------------- 1 | name: Django CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | max-parallel: 4 15 | matrix: 16 | python-version: [3.8.10] 17 | services: 18 | postgres: 19 | image: postgres 20 | env: 21 | POSTGRES_USER: postgres 22 | POSTGRES_PASSWORD: postgres 23 | POSTGRES_DB: github-actions 24 | ports: 25 | - 5432:5432 26 | options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 27 | steps: 28 | - uses: actions/checkout@v2 29 | - name: Cache dependency 30 | uses: actions/cache@v2 31 | with: 32 | path: ~/.cache/pip 33 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 34 | restore-keys: | 35 | ${{ runner.os }}-pip- 36 | - name: Set up Python ${{ matrix.python-version }} 37 | uses: actions/setup-python@v2 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | - name: Install Dependencies 41 | run: | 42 | sudo apt-get install python3-psycopg2 43 | python -m pip install --upgrade pip 44 | pip install -r requirements.txt 45 | pip install psycopg2-binary psycopg2 46 | - name: Coverage report 47 | run: | 48 | cd codedigger 49 | cp codedigger/.env.example codedigger/.env 50 | pip install coverage 51 | coverage run manage.py test 52 | coverage report 53 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: Labeler 9 | on: [pull_request] 10 | 11 | jobs: 12 | label: 13 | 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | 19 | steps: 20 | - uses: actions/labeler@v2 21 | with: 22 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/django 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=django 4 | 5 | ### Django ### 6 | *.log 7 | *.pot 8 | *.pyc 9 | .vscode/ 10 | __pycache__/ 11 | local_settings.py 12 | db.sqlite3 13 | db.sqlite3-journal 14 | media 15 | 16 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 17 | # in your Git repository. Update and uncomment the following line accordingly. 18 | # /staticfiles/ 19 | 20 | ### Django.Python Stack ### 21 | # Byte-compiled / optimized / DLL files 22 | *.py[cod] 23 | *$py.class 24 | 25 | # C extensions 26 | *.so 27 | 28 | # Distribution / packaging 29 | .Python 30 | build/ 31 | develop-eggs/ 32 | dist/ 33 | downloads/ 34 | eggs/ 35 | .eggs/ 36 | lib/ 37 | lib64/ 38 | parts/ 39 | sdist/ 40 | var/ 41 | wheels/ 42 | pip-wheel-metadata/ 43 | share/python-wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | MANIFEST 48 | 49 | # PyInstaller 50 | # Usually these files are written by a python script from a template 51 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 52 | *.manifest 53 | *.spec 54 | 55 | # Installer logs 56 | pip-log.txt 57 | pip-delete-this-directory.txt 58 | 59 | # Unit test / coverage reports 60 | htmlcov/ 61 | .tox/ 62 | .nox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | *.py,cover 70 | .hypothesis/ 71 | .pytest_cache/ 72 | pytestdebug.log 73 | 74 | # Translations 75 | *.mo 76 | 77 | # Django stuff: 78 | 79 | # Flask stuff: 80 | instance/ 81 | .webassets-cache 82 | 83 | # Scrapy stuff: 84 | .scrapy 85 | 86 | # Sphinx documentation 87 | docs/_build/ 88 | doc/_build/ 89 | 90 | # PyBuilder 91 | target/ 92 | 93 | # Jupyter Notebook 94 | .ipynb_checkpoints 95 | 96 | # IPython 97 | profile_default/ 98 | ipython_config.py 99 | 100 | # pyenv 101 | .python-version 102 | 103 | # pipenv 104 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 105 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 106 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 107 | # install all needed dependencies. 108 | #Pipfile.lock 109 | 110 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 111 | __pypackages__/ 112 | 113 | # Celery stuff 114 | celerybeat-schedule 115 | celerybeat.pid 116 | 117 | # SageMath parsed files 118 | *.sage.py 119 | 120 | # Environments 121 | .env 122 | .venv 123 | env/ 124 | venv/ 125 | ENV/ 126 | env.bak/ 127 | venv.bak/ 128 | pythonenv* 129 | 130 | # Spyder project settings 131 | .spyderproject 132 | .spyproject 133 | 134 | # Rope project settings 135 | .ropeproject 136 | 137 | # mkdocs documentation 138 | /site 139 | 140 | # mypy 141 | .mypy_cache/ 142 | .dmypy.json 143 | dmypy.json 144 | 145 | # Pyre type checker 146 | .pyre/ 147 | 148 | # pytype static type analyzer 149 | .pytype/ 150 | 151 | # profiling data 152 | .prof 153 | 154 | # Pycharm Files 155 | .idea/ 156 | .DS_Store 157 | 158 | # End of https://www.toptal.com/developers/gitignore/api/django 159 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/CHANGELOG.md -------------------------------------------------------------------------------- /codedigger/atcoder/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/atcoder/__init__.py -------------------------------------------------------------------------------- /codedigger/atcoder/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /codedigger/atcoder/api.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from user.exception import ValidationException 3 | 4 | BASEURL = "https://kenkoooo.com/atcoder/" 5 | 6 | 7 | def validated_response(response): 8 | if response.status_code != 200: 9 | raise ValidationException('Kenkoooo API: Bad Request') 10 | return response.json() 11 | 12 | 13 | def get_all_contests(): 14 | url = BASEURL + "resources/contests.json" 15 | res = requests.get(url) 16 | return validated_response(res) 17 | 18 | 19 | def get_all_problems(): 20 | url = BASEURL + "resources/problems.json" 21 | res = requests.get(url) 22 | return validated_response(res) 23 | 24 | 25 | def get_all_problems_models(): 26 | url = BASEURL + "resources/problem-models.json" 27 | res = requests.get(url) 28 | return validated_response(res) 29 | 30 | 31 | def get_user_results(handle): 32 | url = BASEURL + "atcoder-api/results" 33 | param = {'user': handle} 34 | res = requests.get(url, params=param) 35 | return validated_response(res) 36 | -------------------------------------------------------------------------------- /codedigger/atcoder/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AtcoderConfig(AppConfig): 5 | name = 'atcoder' 6 | -------------------------------------------------------------------------------- /codedigger/atcoder/cron.py: -------------------------------------------------------------------------------- 1 | from django.core.checks.messages import Error 2 | from utils.email import send_error_mail, send_testing_mail 3 | from utils.common import rating_to_difficulty 4 | 5 | from problem.models import Problem, atcoder_contest 6 | 7 | from .model_utils import create_or_update_contest, create_or_update_problem 8 | from .api import (get_all_contests, get_all_problems, get_all_problems_models) 9 | 10 | 11 | def update_atcoder(): 12 | send_testing_mail('Atcoder Problem Update Process Started') 13 | 14 | try: 15 | data = get_all_contests() 16 | except Exception as e: 17 | send_error_mail('Atcoder Problem Update Kenkoo API All Contests Error', 18 | e) 19 | return 20 | 21 | for contest in data: 22 | create_or_update_contest(contest) 23 | 24 | try: 25 | data = get_all_problems() 26 | except Exception as e: 27 | send_error_mail('Atcoder Problem Update Kenkoo API All Problems Error', 28 | e) 29 | return 30 | 31 | for prob in data: 32 | create_or_update_problem(prob) 33 | 34 | data = get_all_problems_models() 35 | problems = Problem.objects.filter(difficulty=None, platform='A') 36 | 37 | for prob in problems: 38 | if prob.prob_id in data: 39 | if 'difficulty' in data[prob.prob_id]: 40 | old = data[prob.prob_id]['difficulty'] 41 | if old < -1000: 42 | NewValue = (((old + 10000) * 50) / 9000) + 800 43 | elif old <= 0: 44 | NewValue = (((old + 1000) * 350) / 1000) + 850 45 | else: 46 | NewValue = ((old * 2400) / 5000) + 1200 47 | 48 | prob.rating = str(int(NewValue)) 49 | prob.difficulty = rating_to_difficulty(int(NewValue)) 50 | prob.save() 51 | 52 | send_testing_mail('Atcoder Problem Update Process Finished') 53 | -------------------------------------------------------------------------------- /codedigger/atcoder/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/atcoder/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/atcoder/model_utils.py: -------------------------------------------------------------------------------- 1 | from problem.models import Problem, atcoder_contest 2 | 3 | 4 | def create_or_update_contest(contest): 5 | new_contest = atcoder_contest.objects.get_or_create( 6 | contestId=contest['id'], 7 | defaults={ 8 | 'name': contest['title'], 9 | 'startTime': contest['start_epoch_second'], 10 | 'duration': contest['duration_second'] 11 | }) 12 | cur = atcoder_contest.objects.get(contestId=contest['id']) 13 | print(cur) 14 | 15 | 16 | def create_or_update_problem(problem): 17 | new_problem = Problem.objects.get_or_create( 18 | prob_id=problem['id'], 19 | platform='A', 20 | defaults={ 21 | 'name': 22 | problem['title'], 23 | 'contest_id': 24 | problem['contest_id'], 25 | 'url': 26 | "https://atcoder.jp/contests/{}/tasks/{}".format( 27 | problem['contest_id'], problem['id']), 28 | 'index': 29 | problem['id'].split("_")[-1] 30 | }) 31 | -------------------------------------------------------------------------------- /codedigger/atcoder/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /codedigger/atcoder/scrapers.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from user.exception import ValidationException 3 | 4 | BASEURL = "https://atcoder.jp" 5 | 6 | 7 | def get_user_history(handle): 8 | url = BASEURL + "/users/" + handle + "/history" 9 | res = requests.get(url) 10 | if res.status_code != 200: 11 | raise ValidationException('User not found in Atcoder') 12 | return res 13 | 14 | 15 | def get_user_profile(handle): 16 | url = BASEURL + "/users/" + handle 17 | res = requests.get(url) 18 | if res.status_code != 200: 19 | raise ValidationException('User not found in Atcoder') 20 | return res 21 | -------------------------------------------------------------------------------- /codedigger/atcoder/scrapers_utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | import json 3 | import requests 4 | from bs4 import BeautifulSoup as bs4 5 | 6 | 7 | def get_all_contests_details(content): 8 | contests_details = set() 9 | soup = bs4(content, 'html5lib') 10 | contestTable = soup.find('table', {'id': 'history'}) 11 | del soup 12 | if contestTable != None: 13 | contests = contestTable.find('tbody').findAll('tr') 14 | del contestTable 15 | for contest in contests: 16 | contests_details.add( 17 | contest.findAll('td')[1].find('a')['href'].split('/')[-1]) 18 | del contests 19 | return contests_details -------------------------------------------------------------------------------- /codedigger/atcoder/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from problem.models import Problem, atcoder_contest 3 | 4 | 5 | class AtcoderProblemSerializer(serializers.ModelSerializer): 6 | 7 | status = serializers.SerializerMethodField() 8 | 9 | def get_status(self, obj): 10 | 11 | if obj.prob_id in self.context.get('solved'): 12 | return 'solved' 13 | elif obj.prob_id in self.context.get('wrong'): 14 | return 'wrong' 15 | else: 16 | return 'not_attempt' 17 | 18 | class Meta: 19 | model = Problem 20 | fields = [ 21 | 'name', 'url', 'prob_id', 'tags', 'contest_id', 'rating', 'index', 22 | 'platform', 'difficulty', 'editorial', 'status' 23 | ] 24 | 25 | 26 | class AtcoderUpsolveContestSerializer(serializers.ModelSerializer): 27 | 28 | problems = serializers.SerializerMethodField() 29 | 30 | def get_problems(self, obj): 31 | qs = Problem.objects.filter(platform='A', 32 | contest_id=obj.contestId).order_by('index') 33 | return AtcoderProblemSerializer(qs, 34 | many=True, 35 | context={ 36 | 'solved': 37 | self.context.get('solved'), 38 | 'wrong': self.context.get('wrong') 39 | }).data 40 | 41 | class Meta: 42 | 43 | model = atcoder_contest 44 | fields = ['name', 'contestId', 'startTime', 'duration', 'problems'] 45 | -------------------------------------------------------------------------------- /codedigger/atcoder/tests/test_api.py: -------------------------------------------------------------------------------- 1 | from django import test 2 | from .test_setup import TestSetUp 3 | from ..api import * 4 | 5 | 6 | class Test(TestSetUp): 7 | 8 | def test_atcoder_get_all_contests(self): 9 | res = get_all_contests() 10 | self.assertEqual(res[0]["id"], "APG4b") 11 | 12 | def test_atcoder_get_all_problems(self): 13 | res = get_all_problems() 14 | self.assertEqual(res[0]["id"], "APG4b_a") 15 | 16 | def test_atcoder_get_all_problems_models(self): 17 | res = get_all_problems_models() 18 | self.assertEqual(res["abc138_a"]["difficulty"], -848) 19 | 20 | def test_atcoder_get_user_results(self): 21 | res = get_user_results('amann') 22 | self.assertEqual(res[0]["id"], 24029123) 23 | -------------------------------------------------------------------------------- /codedigger/atcoder/tests/test_scrapers.py: -------------------------------------------------------------------------------- 1 | from django import test 2 | from .test_setup import TestSetUp 3 | from ..scrapers import get_user_history 4 | 5 | 6 | class Test(TestSetUp): 7 | 8 | def test_atcoder_scraper(self): 9 | res1 = get_user_history('amann') 10 | res2 = get_user_history('ewqe1e102931ndsmahwn1e1e') 11 | self.assertEqual(res1.status_code, 200) 12 | self.assertEqual(res2.status_code, 404) 13 | -------------------------------------------------------------------------------- /codedigger/atcoder/tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APITestCase 2 | from rest_framework.test import APIClient 3 | from django.urls import reverse 4 | 5 | from user.models import User, Profile 6 | from lists.test_fixtures.profile_fixtures import profile1, profile2 7 | 8 | 9 | class TestSetUp(APITestCase): 10 | fixtures = ["user.json", "at_problems.json", "atcoder_contests.json"] 11 | 12 | @classmethod 13 | def setUpTestData(cls): 14 | Profile.objects.filter(owner=1).update(**profile1) 15 | Profile.objects.filter(owner=2).update(**profile2) 16 | 17 | def setUp(self): 18 | self.login_url = reverse('login') 19 | self.user_data = {'username': 'testing', 'password': 'QWERTY@123'} 20 | return super().setUp() 21 | 22 | @classmethod 23 | def login(self, client, login_url, user_data): 24 | user = User.objects.get(username=user_data['username']) 25 | user.set_password(user_data['password']) 26 | user.save() 27 | response = client.post(login_url, user_data, format="json") 28 | return response.data['tokens']['access'] 29 | 30 | @classmethod 31 | def get_authenticated_client(self, token): 32 | client = APIClient() 33 | client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) 34 | return client 35 | 36 | def tearDown(self): 37 | return super().tearDown() 38 | -------------------------------------------------------------------------------- /codedigger/atcoder/tests/test_upsolve.py: -------------------------------------------------------------------------------- 1 | from django import test 2 | from .test_setup import TestSetUp 3 | from django.urls import reverse 4 | 5 | 6 | class TestUpsolve(TestSetUp): 7 | 8 | def test_atcoder_upsolve(self): 9 | test_url = reverse('at_upsolve') + '?practice=true' 10 | token = self.login(self.client, self.login_url, self.user_data) 11 | client = self.get_authenticated_client(token) 12 | res = client.get(test_url, format="json") 13 | self.assertEqual(len(res.data['result'][0]['problems']), 5) 14 | self.assertEqual(res.data['meta']['total'], 1) 15 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 16 | 17 | def test_atcoder_without_auth_upsolve(self): 18 | test_url = reverse('at_upsolve') + '?practice=true&handle=aaradhya0707' 19 | res = self.client.get(test_url, format="json") 20 | self.assertEqual(len(res.data['result'][0]['problems']), 5) 21 | self.assertEqual(res.data['meta']['total'], 1) 22 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 23 | 24 | def test_atcoder_without_auth_upsolve_wrong_handle(self): 25 | test_url = reverse( 26 | 'at_upsolve') + '?practice=true&handle=aaradhya070707' 27 | res = self.client.get(test_url, format="json") 28 | self.assertEqual(res.status_code, 400) 29 | self.assertEqual(res.data['error'], 'User not found in Atcoder') 30 | -------------------------------------------------------------------------------- /codedigger/atcoder/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import * 4 | 5 | urlpatterns = [ 6 | path('upsolve', ATUpsolveContestAPIView.as_view(), name='at_upsolve') 7 | ] 8 | -------------------------------------------------------------------------------- /codedigger/atcoder/utils.py: -------------------------------------------------------------------------------- 1 | from .api import get_user_results 2 | from .scrapers_utils import get_all_contests_details 3 | from .scrapers import get_user_history 4 | 5 | 6 | def atcoder_status(handle): 7 | contests_details = set() 8 | all_contest = set() 9 | solved = set() 10 | wrong = set() 11 | 12 | try: 13 | res = get_user_history(handle) 14 | except: 15 | return (contests_details, all_contest, solved, wrong) 16 | 17 | contests_details = get_all_contests_details(res.content) 18 | 19 | try: 20 | data = get_user_results(handle) 21 | except: 22 | return (contests_details, all_contest, solved, wrong) 23 | 24 | for sub in data: 25 | all_contest.add(sub["contest_id"]) 26 | if sub["result"] == "AC": 27 | solved.add(sub["problem_id"]) 28 | 29 | for sub in data: 30 | if sub["result"] != "AC" and sub["problem_id"] not in solved: 31 | wrong.add(sub["problem_id"]) 32 | 33 | return (contests_details, all_contest, solved, wrong) 34 | -------------------------------------------------------------------------------- /codedigger/atcoder/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.response import Response 2 | from rest_framework import generics, mixins 3 | 4 | # Django Models Stuff 5 | from problem.models import atcoder_contest 6 | from user.models import Profile 7 | 8 | # Serializer and Extra Utils Function 9 | from .serializers import AtcoderUpsolveContestSerializer 10 | from problem.utils import atcoder_status, get_page_number, get_upsolve_response_dict 11 | from user.permissions import * 12 | from user.exception import ValidationException 13 | from lists.utils import get_total_page, getqs 14 | from .scrapers import get_user_profile 15 | # Create your views here. 16 | 17 | 18 | class ATUpsolveContestAPIView( 19 | mixins.CreateModelMixin, 20 | generics.ListAPIView, 21 | ): 22 | permission_classes = [AuthenticatedOrReadOnly] 23 | 24 | serializer_class = AtcoderUpsolveContestSerializer 25 | 26 | def get(self, request): 27 | 28 | is_auth = self.request.user.is_authenticated 29 | handle = "" 30 | if not is_auth: 31 | handle = request.GET.get('handle', None) 32 | if handle == None: 33 | raise ValidationException( 34 | 'Any of handle or Bearer Token is required.') 35 | get_user_profile(handle) 36 | else: 37 | handle = Profile.objects.get(owner=self.request.user).atcoder 38 | 39 | if handle == "" or handle == None: 40 | raise ValidationException( 41 | 'You haven\'t Entered your Atcoder Handle in your Profile.. Update Now!' 42 | ) 43 | 44 | practice = request.GET.get('practice') 45 | page = request.GET.get('page', None) 46 | per_page = request.GET.get('per_page', '10') 47 | path = request.build_absolute_uri('/atcoder/upsolve?') 48 | 49 | if not is_auth: 50 | path = '{}handle={};'.format(path, handle) 51 | 52 | if practice != None: 53 | path = '{}practice={};'.format(path, practice) 54 | 55 | per_page = get_page_number(per_page) 56 | page = get_page_number(page) 57 | 58 | contests_details, all_contest, solved, wrong = atcoder_status(handle) 59 | 60 | if practice == 'true': 61 | contests_details = contests_details.union(all_contest) 62 | data = {'solved': solved, 'wrong': wrong} 63 | qs = atcoder_contest.objects.filter( 64 | contestId__in=contests_details).order_by('-startTime') 65 | 66 | total_contest = qs.count() 67 | if total_contest == 0: 68 | return Response({'status': 'OK', 'result': []}) 69 | total_page = get_total_page(total_contest, per_page) 70 | if page > total_page: 71 | raise ValidationException('Page Out of Bound') 72 | 73 | qs = getqs(qs, per_page, page) 74 | user_contest_details = AtcoderUpsolveContestSerializer( 75 | qs, many=True, context=data).data 76 | res = get_upsolve_response_dict(user_contest_details, path, page, 77 | total_contest, per_page) 78 | return Response(res) 79 | -------------------------------------------------------------------------------- /codedigger/blog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/blog/__init__.py -------------------------------------------------------------------------------- /codedigger/blog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from .models import Category, Blog 5 | 6 | admin.site.register(Category) 7 | admin.site.register(Blog) 8 | -------------------------------------------------------------------------------- /codedigger/blog/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BlogConfig(AppConfig): 5 | name = 'blog' 6 | -------------------------------------------------------------------------------- /codedigger/blog/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-02 15:48 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Category', 19 | fields=[ 20 | ('id', 21 | models.AutoField(auto_created=True, 22 | primary_key=True, 23 | serialize=False, 24 | verbose_name='ID')), 25 | ('name', models.CharField(blank=True, max_length=50, 26 | null=True)), 27 | ('slug', models.CharField(max_length=55, unique=True)), 28 | ], 29 | ), 30 | migrations.CreateModel( 31 | name='Blog', 32 | fields=[ 33 | ('id', 34 | models.AutoField(auto_created=True, 35 | primary_key=True, 36 | serialize=False, 37 | verbose_name='ID')), 38 | ('title', models.CharField(max_length=100)), 39 | ('slug', models.SlugField(max_length=110, unique=True)), 40 | ('body', 41 | models.CharField(blank=True, max_length=5000, null=True)), 42 | ('status', 43 | models.CharField(choices=[('0', 'Draft'), ('1', 'Pending'), 44 | ('2', 'Published')], 45 | default='0', 46 | max_length=1)), 47 | ('views', models.IntegerField(default=0)), 48 | ('meta_title', 49 | models.CharField(blank=True, max_length=100, null=True)), 50 | ('meta_desc', 51 | models.CharField(blank=True, max_length=200, null=True)), 52 | ('created_at', models.DateTimeField(auto_now_add=True)), 53 | ('updated_at', models.DateTimeField(auto_now=True)), 54 | ('category', 55 | models.ForeignKey( 56 | blank=True, 57 | null=True, 58 | on_delete=django.db.models.deletion.SET_NULL, 59 | to='blog.category')), 60 | ('user', 61 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 62 | to=settings.AUTH_USER_MODEL)), 63 | ], 64 | ), 65 | ] 66 | -------------------------------------------------------------------------------- /codedigger/blog/migrations/0002_blog_youtube_link.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-02 15:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('blog', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='blog', 15 | name='youtube_link', 16 | field=models.CharField(blank=True, max_length=100, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/blog/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/blog/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/blog/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.template.defaultfilters import slugify 3 | from user.models import User 4 | 5 | # Create your models here. 6 | 7 | 8 | class Category(models.Model): 9 | name = models.CharField(max_length=50, blank=True, null=True) 10 | slug = models.CharField(max_length=55, unique=True) 11 | 12 | def __str__(self): 13 | return self.name 14 | 15 | def save(self, **kwargs): 16 | self.slug = slugify(self.name) 17 | super(Category, self).save() 18 | 19 | 20 | class Blog(models.Model): 21 | STATUS = (('0', 'Draft'), ('1', 'Pending'), ('2', 'Published')) 22 | user = models.ForeignKey(User, on_delete=models.CASCADE) 23 | category = models.ForeignKey(Category, 24 | on_delete=models.SET_NULL, 25 | blank=True, 26 | null=True) 27 | title = models.CharField(max_length=100) 28 | slug = models.SlugField(max_length=110, unique=True) 29 | body = models.CharField(max_length=5000, blank=True, null=True) 30 | status = models.CharField(max_length=1, choices=STATUS, default='0') 31 | views = models.IntegerField(default=0) 32 | meta_title = models.CharField(max_length=100, blank=True, null=True) 33 | meta_desc = models.CharField(max_length=200, blank=True, null=True) 34 | created_at = models.DateTimeField(auto_now_add=True) 35 | updated_at = models.DateTimeField(auto_now=True) 36 | youtube_link = models.CharField(max_length=100, blank=True, null=True) 37 | 38 | def __str__(self): 39 | return self.slug 40 | 41 | def save(self, **kwargs): 42 | self.slug = slugify(self.title) 43 | super(Blog, self).save() 44 | -------------------------------------------------------------------------------- /codedigger/blog/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Blog, Category 3 | 4 | # Add your Serializers here 5 | 6 | 7 | class CategorySerializer(serializers.ModelSerializer): 8 | 9 | class Meta: 10 | model = Category 11 | fields = ['name', 'slug'] 12 | 13 | 14 | class BlogSerializer(serializers.ModelSerializer): 15 | 16 | category = CategorySerializer() 17 | user = serializers.SerializerMethodField() 18 | 19 | def get_user(self, obj): 20 | return obj.user.username 21 | 22 | class Meta: 23 | model = Blog 24 | fields = [ 25 | 'user', 'category', 'title', 'slug', 'created_at', 'updated_at' 26 | ] 27 | 28 | 29 | class ABlogSerializer(serializers.ModelSerializer): 30 | 31 | category = CategorySerializer() 32 | user = serializers.SerializerMethodField() 33 | 34 | def get_user(self, obj): 35 | return obj.user.username 36 | 37 | class Meta: 38 | model = Blog 39 | fields = [ 40 | 'user', 'category', 'title', 'slug', 'body', 'views', 'meta_title', 41 | 'meta_desc', 'created_at', 'updated_at', 'youtube_link' 42 | ] 43 | -------------------------------------------------------------------------------- /codedigger/blog/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /codedigger/blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.BlogAPIView.as_view(), name='blog.index'), 7 | path('', views.ABlogAPIView.as_view(), name='blog.information'), 8 | ] -------------------------------------------------------------------------------- /codedigger/blog/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | from rest_framework.views import APIView 5 | from rest_framework.response import Response 6 | from rest_framework import generics, mixins, permissions, status 7 | 8 | from .models import Blog 9 | from .serializers import BlogSerializer, ABlogSerializer 10 | from user.permissions import * 11 | 12 | 13 | class BlogAPIView(mixins.CreateModelMixin, generics.ListAPIView): 14 | permission_classes = [AuthenticatedOrReadOnly] 15 | #authentication_classes = [SessionAuthentication] 16 | serializer_class = BlogSerializer 17 | 18 | #passed_id = None 19 | #running queries and stuff 20 | def get(self, request): 21 | return Response({ 22 | 'status': 23 | 'OK', 24 | 'result': 25 | BlogSerializer(Blog.objects.all(), many=True).data 26 | }) 27 | 28 | 29 | class ABlogAPIView(mixins.CreateModelMixin, generics.ListAPIView): 30 | permission_classes = [AuthenticatedOrReadOnly] 31 | #authentication_classes = [SessionAuthentication] 32 | serializer_class = ABlogSerializer 33 | 34 | #passed_id = None 35 | 36 | #running queries and stuff 37 | def get(self, request, slug): 38 | 39 | qs = Blog.objects.filter(slug=slug) 40 | if qs.exists(): 41 | return Response({ 42 | 'status': 'OK', 43 | 'result': ABlogSerializer(qs[0]).data 44 | }) 45 | else: 46 | return Response( 47 | { 48 | 'status': 'FAILED', 49 | 'error': 'Requested Blog doesn\'t exists.' 50 | }, 51 | status=status.HTTP_400_BAD_REQUEST) 52 | -------------------------------------------------------------------------------- /codedigger/codechef/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codechef/__init__.py -------------------------------------------------------------------------------- /codedigger/codechef/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import CodechefContest, CodechefContestProblems 3 | # Register your models here. 4 | admin.site.register(CodechefContest) 5 | admin.site.register(CodechefContestProblems) 6 | -------------------------------------------------------------------------------- /codedigger/codechef/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CodechefConfig(AppConfig): 5 | name = 'codechef' 6 | -------------------------------------------------------------------------------- /codedigger/codechef/cron.py: -------------------------------------------------------------------------------- 1 | from codechef.scraper_utils import ContestData, ProblemData 2 | from codechef.model_utils import create_or_update_codechefContest, create_or_update_codechefProblem 3 | 4 | 5 | def update_AllContests(): 6 | # Creates new contests and problems in Database 7 | all_contests = ContestData('past') 8 | for contest in all_contests: 9 | create_or_update_codechefContest(contest) 10 | contest_problems_info = ProblemData(contest['ContestCode']) 11 | create_or_update_codechefProblem(contest_problems_info) 12 | -------------------------------------------------------------------------------- /codedigger/codechef/fixtures/cc_contest_problem.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "codechef.codechefcontestproblems", 4 | "pk": 1, 5 | "fields": { 6 | "contest": 1, 7 | "problem": 11 8 | } 9 | }, 10 | { 11 | "model": "codechef.codechefcontestproblems", 12 | "pk": 2, 13 | "fields": { 14 | "contest": 1, 15 | "problem": 12 16 | } 17 | }, 18 | { 19 | "model": "codechef.codechefcontestproblems", 20 | "pk": 3, 21 | "fields": { 22 | "contest": 1, 23 | "problem": 13 24 | } 25 | }, 26 | { 27 | "model": "codechef.codechefcontestproblems", 28 | "pk": 4, 29 | "fields": { 30 | "contest": 1, 31 | "problem": 14 32 | } 33 | }, 34 | { 35 | "model": "codechef.codechefcontestproblems", 36 | "pk": 5, 37 | "fields": { 38 | "contest": 1, 39 | "problem": 15 40 | } 41 | }, 42 | { 43 | "model": "codechef.codechefcontestproblems", 44 | "pk": 6, 45 | "fields": { 46 | "contest": 2, 47 | "problem": 16 48 | } 49 | }, 50 | { 51 | "model": "codechef.codechefcontestproblems", 52 | "pk": 7, 53 | "fields": { 54 | "contest": 2, 55 | "problem": 17 56 | } 57 | }, 58 | { 59 | "model": "codechef.codechefcontestproblems", 60 | "pk": 8, 61 | "fields": { 62 | "contest": 2, 63 | "problem": 18 64 | } 65 | } 66 | ] -------------------------------------------------------------------------------- /codedigger/codechef/fixtures/cc_contests.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"codechef.codechefcontest", 4 | "pk":1, 5 | "fields":{ 6 | "name":"April Challenge", 7 | "contestId":"APRIL20B" 8 | } 9 | }, 10 | { 11 | "model":"codechef.codechefContest", 12 | "pk":2, 13 | "fields":{ 14 | "name":"July CookOff", 15 | "contestId":"COOK120B" 16 | } 17 | }, 18 | { 19 | "model":"codechef.codechefContest", 20 | "pk":3, 21 | "fields":{ 22 | "name":"September Challenge", 23 | "contestId":"SEPT20B" 24 | } 25 | } 26 | ] -------------------------------------------------------------------------------- /codedigger/codechef/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-12-19 04:35 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('problem', '0005_auto_20210129_2145'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='CodechefContest', 18 | fields=[ 19 | ('id', 20 | models.AutoField(auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name='ID')), 24 | ('name', models.CharField(max_length=200)), 25 | ('contestId', models.CharField(db_index=True, max_length=10)), 26 | ('duration', models.IntegerField(blank=True, null=True)), 27 | ('startTime', models.DateTimeField(blank=True, null=True)), 28 | ('url', models.CharField(blank=True, max_length=200, 29 | null=True)), 30 | ], 31 | ), 32 | migrations.CreateModel( 33 | name='CodechefContestProblems', 34 | fields=[ 35 | ('id', 36 | models.AutoField(auto_created=True, 37 | primary_key=True, 38 | serialize=False, 39 | verbose_name='ID')), 40 | ('contest', 41 | models.ForeignKey(blank=True, 42 | on_delete=django.db.models.deletion.CASCADE, 43 | to='codechef.codechefcontest')), 44 | ('problem', 45 | models.ForeignKey(blank=True, 46 | on_delete=django.db.models.deletion.CASCADE, 47 | to='problem.problem')), 48 | ], 49 | ), 50 | ] 51 | -------------------------------------------------------------------------------- /codedigger/codechef/migrations/0002_auto_20211227_1026.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-12-27 04:56 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codechef', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='codechefcontest', 15 | name='name', 16 | field=models.CharField(blank=True, max_length=200), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/codechef/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codechef/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/codechef/model_utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from problem.models import Problem 3 | from codechef.models import CodechefContest, CodechefContestProblems 4 | 5 | 6 | def create_or_update_codechefProblem(problemdata): 7 | for problem in problemdata: 8 | Prob, created = Problem.objects.get_or_create( 9 | name=problem['Name'], 10 | prob_id=problem['ProblemCode'], 11 | url=problem['ProblemURL'], 12 | contest_id=problem['ContestId'], 13 | platform=problem['Platform']) 14 | cont = CodechefContest.objects.get(contestId=problem['ContestId']) 15 | prob = Problem.objects.get(prob_id=problem['ProblemCode'], 16 | contest_id=problem['ContestId']) 17 | ccprob, created = CodechefContestProblems.objects.get_or_create( 18 | contest=cont, problem=prob) 19 | 20 | 21 | def create_or_update_codechefContest(contest): 22 | contestDate = datetime.strptime(contest['StartTime'], "%d %B %Y %H:%M:%S") 23 | cont = CodechefContest.objects.get_or_create( 24 | name=contest['Name'], 25 | contestId=contest['ContestCode'], 26 | duration=contest['Duration'], 27 | startTime=contestDate, 28 | url=contest['ContestURL']) 29 | 30 | # create_or_update_codechefProblem(contest_problems_info) 31 | -------------------------------------------------------------------------------- /codedigger/codechef/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from problem.models import Problem 3 | 4 | 5 | # Create your models here. 6 | class CodechefContest(models.Model): 7 | 8 | name = models.CharField(max_length=200, blank=True) 9 | contestId = models.CharField(max_length=10, db_index=True) 10 | duration = models.IntegerField(blank=True, null=True) 11 | startTime = models.DateTimeField(blank=True, null=True) 12 | url = models.CharField(max_length=200, blank=True, null=True) 13 | 14 | def __str__(self): 15 | return self.contestId 16 | 17 | 18 | class CodechefContestProblems(models.Model): 19 | 20 | contest = models.ForeignKey(CodechefContest, 21 | blank=True, 22 | on_delete=models.CASCADE) 23 | problem = models.ForeignKey(Problem, blank=True, on_delete=models.CASCADE) 24 | 25 | def __str__(self): 26 | return self.problem.prob_id 27 | -------------------------------------------------------------------------------- /codedigger/codechef/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from problem.models import Problem 3 | from codechef.models import CodechefContest, CodechefContestProblems 4 | from problem.serializers import CCUpsolveContestSerializer, ProbSerializer 5 | 6 | 7 | class CodechefUpsolveSerializer(serializers.ModelSerializer): 8 | 9 | problems = serializers.SerializerMethodField() 10 | 11 | def get_problems(self, obj): 12 | context = { 13 | 'upsolved': self.context.get('upsolved'), 14 | 'solved': self.context.get('solved') 15 | } 16 | pr = Problem.objects.filter( 17 | codechefcontestproblems__in=CodechefContestProblems.objects.filter( 18 | contest=obj)) 19 | return CCUpsolveContestSerializer(pr, many=True, context=context).data 20 | 21 | class Meta: 22 | model = CodechefContest 23 | 24 | fields = [ 25 | 'name', 26 | 'contestId', 27 | 'duration', 28 | 'startTime', 29 | 'problems', 30 | ] 31 | -------------------------------------------------------------------------------- /codedigger/codechef/test_fixtures/model_utils_fixture.py: -------------------------------------------------------------------------------- 1 | codechef_contest = { 2 | "Name": "July Cook-Off 2021", 3 | "ContestCode": "COOK117B", 4 | "Duration": 165, 5 | "StartTime": "28 July 2021 20:00:00", 6 | "ContestURL": "https://www.codechef.com/COOK131B" 7 | } 8 | 9 | codechef_problem = [{ 10 | 'Name': 'Lift Requests', 11 | 'ProblemCode': 'LIFTME', 12 | 'ProblemURL': 'https://www.codechef.com/COOK117B/problems/LIFTME', 13 | 'ContestId': 'COOK117B', 14 | 'Platform': 'C' 15 | }, { 16 | 'Name': 'Inversions in Permutations', 17 | 'ProblemCode': 'KPERM', 18 | 'ProblemURL': 'https://www.codechef.com/COOK117B/problems/KPERM', 19 | 'ContestId': 'COOK117B', 20 | 'Platform': 'C' 21 | }, { 22 | 'Name': 'Matrix Decomposition', 23 | 'ProblemCode': 'MATBREAK', 24 | 'ProblemURL': 'https://www.codechef.com/COOK117B/problems/MATBREAK', 25 | 'ContestId': 'COOK117B', 26 | 'Platform': 'C' 27 | }, { 28 | 'Name': 'Simple Operations', 29 | 'ProblemCode': 'MINOPS', 30 | 'ProblemURL': 'https://www.codechef.com/COOK117B/problems/MINOPS', 31 | 'ContestId': 'COOK117B', 32 | 'Platform': 'C' 33 | }, { 34 | 'Name': 'The Gifting Problem', 35 | 'ProblemCode': 'KARRAY', 36 | 'ProblemURL': 'https://www.codechef.com/COOK117B/problems/KARRAY', 37 | 'ContestId': 'COOK117B', 38 | 'Platform': 'C' 39 | }, { 40 | 'Name': 'Tower Counting', 41 | 'ProblemCode': 'TOWCNT', 42 | 'ProblemURL': 'https://www.codechef.com/COOK117B/problems/TOWCNT', 43 | 'ContestId': 'COOK117B', 44 | 'Platform': 'C' 45 | }, { 46 | 'Name': 'Maximum GCD Subset', 47 | 'ProblemCode': 'TREESUB', 48 | 'ProblemURL': 'https://www.codechef.com/COOK117B/problems/TREESUB', 49 | 'ContestId': 'COOK117B', 50 | 'Platform': 'C' 51 | }] 52 | -------------------------------------------------------------------------------- /codedigger/codechef/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codechef/tests/__init__.py -------------------------------------------------------------------------------- /codedigger/codechef/tests/test_model_utils.py: -------------------------------------------------------------------------------- 1 | from .test_setup import TestSetUp 2 | from codechef.models import CodechefContest, CodechefContestProblems 3 | from problem.models import Problem 4 | from codechef.model_utils import (create_or_update_codechefContest, 5 | create_or_update_codechefProblem) 6 | from codechef.test_fixtures.model_utils_fixture import (codechef_contest, 7 | codechef_problem) 8 | from datetime import datetime 9 | 10 | 11 | class TestModelUtils(TestSetUp): 12 | 13 | def test_CreateContest(self): 14 | newContest = create_or_update_codechefContest(codechef_contest) 15 | contestDate = datetime.strptime(codechef_contest["StartTime"], 16 | "%d %B %Y %H:%M:%S") 17 | contest = CodechefContest.objects.get( 18 | contestId=codechef_contest["ContestCode"]) 19 | self.assertEqual(contest.url, codechef_contest["ContestURL"]) 20 | self.assertEqual(contest.contestId, 'COOK117B') 21 | 22 | def test_CreateProblem(self): 23 | create_or_update_codechefContest(codechef_contest) 24 | create_or_update_codechefProblem(codechef_problem) 25 | problemModel = Problem.objects.get( 26 | prob_id=codechef_problem[0]['ProblemCode']) 27 | firstProbCode = codechef_problem[0]['ProblemCode'] 28 | codechefProblem = CodechefContestProblems.objects.filter( 29 | problem=problemModel) 30 | self.assertEqual(codechefProblem[0].problem.prob_id, 'LIFTME') 31 | -------------------------------------------------------------------------------- /codedigger/codechef/tests/test_scraper.py: -------------------------------------------------------------------------------- 1 | from .test_setup import TestSetUp 2 | from codechef.scraper import problemScraper 3 | from codechef.scraper_utils import getContestDivision, ProblemData 4 | from codechef.test_fixtures.scraper_utils_fixture import (problemResult, 5 | contestResult, 6 | getDivisionResult1, 7 | getDivisionResult2) 8 | 9 | 10 | class TestScraper(TestSetUp): 11 | 12 | def test_problemScraper(self): 13 | 14 | code = "COOK117B" 15 | 16 | output_result = problemScraper(code) 17 | self.assertEqual("status" in output_result, True, 18 | "Couldn't fetch status result in problemScraper") 19 | self.assertEqual(output_result["status"], "success", 20 | "Failed status in problemScraper") 21 | self.assertEqual("code" in output_result, True, 22 | "problemScraper doesn't contain the contest code") 23 | self.assertEqual("problems" in output_result, True, 24 | "problemScraper doesn't have the problem info") 25 | 26 | def test_ProblemData(self): 27 | code = "COOK130B" 28 | 29 | problems_all = ProblemData(code) 30 | self.assertTrue(len(problems_all) >= 1) 31 | self.assertEqual(problemResult, problems_all) 32 | 33 | def test_getDivisionInfo(self): 34 | code1 = "COOK129" 35 | code2 = "LTIME15" 36 | 37 | output1 = getContestDivision(code1) 38 | output2 = getContestDivision(code2) 39 | 40 | self.assertEqual(output1, getDivisionResult1, 41 | "Incorrect Division Details for test 1!") 42 | self.assertEqual(output2, getDivisionResult2, 43 | "Incorrect Division Details for test 2!") 44 | -------------------------------------------------------------------------------- /codedigger/codechef/tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APITestCase, APIClient 2 | from django.urls import reverse 3 | 4 | from user.models import Profile, User 5 | from lists.test_fixtures.profile_fixtures import profile1, profile2 6 | 7 | 8 | class TestSetUp(APITestCase): 9 | 10 | # def setUp(self): 11 | # # set up test case do some calculation 12 | # return super().setUp() 13 | 14 | fixtures = [ 15 | "user.json", "cc_problems.json", "cc_contests.json", 16 | "cc_contest_problem.json" 17 | ] 18 | 19 | @classmethod 20 | def setUpTestData(cls): 21 | Profile.objects.filter(owner=1).update(**profile1) 22 | Profile.objects.filter(owner=2).update(**profile2) 23 | 24 | def setUp(self): 25 | self.login_url = reverse('login') 26 | self.user_data = {'username': 'testing', 'password': 'QWERTY@123'} 27 | return super().setUp() 28 | 29 | @classmethod 30 | def login(self, client, login_url, user_data): 31 | user = User.objects.get(username=user_data['username']) 32 | user.set_password(user_data['password']) 33 | user.save() 34 | response = client.post(login_url, user_data, format="json") 35 | return response.data['tokens']['access'] 36 | 37 | @classmethod 38 | def get_authenticated_client(self, token): 39 | client = APIClient() 40 | client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) 41 | return client 42 | 43 | def tearDown(self): 44 | return super().tearDown() 45 | -------------------------------------------------------------------------------- /codedigger/codechef/tests/test_upsolve.py: -------------------------------------------------------------------------------- 1 | from django import test 2 | from .test_setup import TestSetUp 3 | from django.urls import reverse 4 | 5 | 6 | class TestUpsolve(TestSetUp): 7 | 8 | def test_codechef_upsolve(self): 9 | test_url = reverse('codechef-upsolve') 10 | token = self.login(self.client, self.login_url, self.user_data) 11 | client = self.get_authenticated_client(token) 12 | res = client.get(test_url, format="json") 13 | self.assertEqual(len(res.data['result'][0]['problems']), 5) 14 | self.assertEqual(res.data['meta']['total'], 3) 15 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 16 | -------------------------------------------------------------------------------- /codedigger/codechef/tests/test_views.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codechef/tests/test_views.py -------------------------------------------------------------------------------- /codedigger/codechef/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | urlpatterns = [ 5 | path('upsolve', 6 | views.CodechefUpsolveAPIView.as_view(), 7 | name="codechef-upsolve"), 8 | path('testing', views.testing) 9 | ] 10 | -------------------------------------------------------------------------------- /codedigger/codechef/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | from rest_framework.response import Response 3 | from rest_framework import generics, serializers 4 | 5 | from user.exception import ValidationException 6 | from .models import CodechefContest 7 | from .serializers import CodechefUpsolveSerializer 8 | from .scraper_utils import contestgivenScrapper, problems_solved 9 | from user.models import Profile 10 | from problem.utils import getqs, get_total_page, get_upsolve_response_dict 11 | from codechef.cron import * 12 | # Create your views here. 13 | 14 | 15 | class CodechefUpsolveAPIView(generics.GenericAPIView): 16 | 17 | serializer_class = CodechefUpsolveSerializer 18 | 19 | def get(self, request): 20 | 21 | page = int(request.GET.get('page', 1)) 22 | per_page = request.GET.get('per_page', 10) 23 | path = request.build_absolute_uri('/codechef/upsolve?') 24 | 25 | try: 26 | handle = Profile.objects.get(owner=self.request.user).codechef 27 | except: 28 | handle = request.GET.get('handle') 29 | path = f"{path}handle={handle}&" 30 | 31 | if handle == None: 32 | raise ValidationException( 33 | 'Any of handle or Bearer Token is required.') 34 | 35 | upsolved, solved = problems_solved(handle) 36 | 37 | data = {'solved': solved, 'upsolved': upsolved} 38 | 39 | contests = contestgivenScrapper(handle) 40 | 41 | conts = CodechefContest.objects.filter(contestId__in=contests) 42 | 43 | total_contest = conts.count() 44 | if total_contest == 0: 45 | return Response({'status': 'OK', 'result': []}) 46 | 47 | total_page = get_total_page(total_contest, per_page) 48 | 49 | if page != None and page > total_page: 50 | raise ValidationException('Page Out of Bound') 51 | 52 | qs = getqs(conts, per_page, page) 53 | result = CodechefUpsolveSerializer(qs, many=True, context=data).data 54 | res = get_upsolve_response_dict(result, path, page, total_contest, 55 | per_page) 56 | return Response(res) 57 | 58 | 59 | def testing(request): 60 | update_AllContests() 61 | return HttpResponse("Successfully Scrapped!") 62 | -------------------------------------------------------------------------------- /codedigger/codedigger/.env.example: -------------------------------------------------------------------------------- 1 | # Django Settings 2 | SECRET_KEY = very_long_secret_key 3 | DEBUG = True 4 | 5 | # Database Settings 6 | DB_NAME = codedigger 7 | DB_USER = root 8 | DB_PASSWORD = 9 | DB_HOST = 127.0.0.1 10 | DB_PORT = 3306 11 | 12 | # Gmail Id and Password Settings 13 | # For sending Mail 14 | EMAIL_HOST_USER = your_mail_id 15 | EMAIL_HOST_PASSWORD = your_password 16 | 17 | # Admin Codeforces Id (For Short Code Contest) 18 | # Fill only if you want to improve short code contest 19 | CODEFORCES_HANDLE = codeforces_handle 20 | CODEFORCES_PASSWORD = codeforces_password -------------------------------------------------------------------------------- /codedigger/codedigger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codedigger/__init__.py -------------------------------------------------------------------------------- /codedigger/codedigger/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for codedigger project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codedigger.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /codedigger/codedigger/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | from drf_yasg.views import get_schema_view 4 | from drf_yasg import openapi 5 | from rest_framework import permissions 6 | 7 | schema_view = get_schema_view( 8 | openapi.Info( 9 | title="CodeDigger API", 10 | default_version='v1', 11 | description= 12 | "This project aims at accumulating the data of competitive programming platforms into one platform - CodeDigger. We have used the Codeforces, Codechef, AtCoder, Spoj and UVA data in our project. We have used their publicly available APIs and introduced several features into out application such as friends, mentors, ladders and upsolve. This can be the ultimate stop for everyone practicing competitive programming. Users can login with email or google and check their rating and best contests in which they have performed on every site and look at the remaining questions in the contests given by them. We have also incorporated features such as ladders which will enable users to solve problems from different sites in a ladder-like fashion. They can also explore problems and filter them according to difficulty, rating topic, solved by friends or mentors, etc as per their needs.", 13 | terms_of_service="https://codedigger.tech/terms", 14 | contact=openapi.Contact(email="contact.codedigger@gmail.com"), 15 | license=openapi.License(name="Apache License 2.0"), 16 | ), 17 | public=True, 18 | permission_classes=(permissions.AllowAny, ), 19 | ) 20 | 21 | urlpatterns = [ 22 | path('admin/', admin.site.urls), 23 | path('auth/', include('user.urls')), 24 | path('social_auth/', 25 | include(('social_auth.urls', 'social_auth'), 26 | namespace="social_auth")), 27 | path('problems/', include('problem.urls')), 28 | path('codeforces/', include('codeforces.urls')), 29 | path('codechef/', include('codechef.urls')), 30 | path('lists/', include('lists.urls')), 31 | path('blog/', include('blog.urls')), 32 | path('contest/', include('contest.urls')), 33 | path('atcoder/', include('atcoder.urls')), 34 | path('', 35 | schema_view.with_ui('swagger', cache_timeout=0), 36 | name='schema-swagger-ui'), 37 | path('redoc/', 38 | schema_view.with_ui('redoc', cache_timeout=0), 39 | name='schema-redoc'), 40 | path('codechef/', include('codechef.urls')), 41 | ] 42 | -------------------------------------------------------------------------------- /codedigger/codedigger/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for codedigger project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codedigger.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /codedigger/codeforces/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codeforces/__init__.py -------------------------------------------------------------------------------- /codedigger/codeforces/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from .models import organization, country, user, contest, user_contest_rank 5 | 6 | admin.site.register(organization) 7 | admin.site.register(country) 8 | admin.site.register(user) 9 | admin.site.register(contest) 10 | admin.site.register(user_contest_rank) 11 | -------------------------------------------------------------------------------- /codedigger/codeforces/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CodeforcesConfig(AppConfig): 5 | name = 'codeforces' 6 | -------------------------------------------------------------------------------- /codedigger/codeforces/codeforcesProblemSet.py: -------------------------------------------------------------------------------- 1 | from problem.models import Problem 2 | from .models import CodeforcesProblemSet 3 | 4 | 5 | def get_parent(problem): 6 | try: 7 | return CodeforcesProblemSet.objects.get(child=problem).parent 8 | except: 9 | return problem 10 | 11 | 12 | def check(problem1, problem2): 13 | return get_parent(problem1).id == get_parent(problem2).id 14 | 15 | 16 | def join(problem1, problem2): 17 | if not check(problem1, problem2): 18 | parent = get_parent(problem1) 19 | child = get_parent(problem2) 20 | CodeforcesProblemSet.objects.filter(parent=child).update(parent=parent) 21 | CodeforcesProblemSet(parent=parent, child=child).save() 22 | 23 | 24 | def get_similar_problems(problem): 25 | parent = get_parent(problem) 26 | childern = list(CodeforcesProblemSet.objects.filter(parent = parent)\ 27 | .values_list('child', flat = True)) 28 | if not childern: 29 | return [] 30 | 31 | childern.append(parent.id) 32 | problem_qs = Problem.objects.filter(id__in = childern)\ 33 | .exclude(id = problem.id) 34 | return problem_qs 35 | -------------------------------------------------------------------------------- /codedigger/codeforces/contestProblem.py: -------------------------------------------------------------------------------- 1 | from .models import contest 2 | from problem.models import Problem 3 | from .api import user_status 4 | from .api_utils import get_all_submission 5 | 6 | 7 | def AssignCodeforcesProblem(cf_user): 8 | 9 | # Set Minimum rating as 1000 and maximum rating as 2500 10 | userRating = cf_user.rating // 100 * 100 11 | userRating = min(max(userRating, 1000), 2500) 12 | 13 | contests = contest.objects.filter(Type__exact='R') 14 | contestIds = [contest.contestId for contest in contests] 15 | 16 | problems = Problem.objects.filter(contest_id__in=contestIds, 17 | platform='F', 18 | difficulty__isnull=False, 19 | rating__gte=userRating - 200, 20 | rating__lte=userRating + 200) 21 | 22 | # Excluding all the submissions that have seen by the user 23 | submissions = user_status(cf_user.handle) 24 | probIds = get_all_submission(submissions) 25 | problems = problems.exclude(prob_id__in=probIds) 26 | 27 | # Assigning the problem as per the order 28 | # rating-200,rating-100,=rating,rating+100,rating+200 29 | AssignedProblem = [] 30 | userRating -= 200 31 | for i in range(5): 32 | for problem in problems: 33 | if problem.rating == userRating: 34 | AssignedProblem.append(problem) 35 | break 36 | userRating += 100 37 | 38 | return AssignedProblem 39 | -------------------------------------------------------------------------------- /codedigger/codeforces/fixtures/cf_contest.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"codeforces.contest", 4 | "pk":1, 5 | "fields":{ 6 | "name":"1549", 7 | "contestId":"1549", 8 | "Type":"R" 9 | } 10 | }, 11 | { 12 | "model":"codeforces.contest", 13 | "pk":2, 14 | "fields":{ 15 | "name":"Codeforces Round #555 Div.2", 16 | "contestId":"1345", 17 | "Type":"R" 18 | } 19 | }, 20 | { 21 | "model":"codeforces.contest", 22 | "pk":3, 23 | "fields":{ 24 | "name":"GYM-1", 25 | "contestId":"100001", 26 | "Type":"G" 27 | } 28 | } 29 | ] -------------------------------------------------------------------------------- /codedigger/codeforces/fixtures/cf_users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"codeforces.user", 4 | "pk":1, 5 | "fields":{ 6 | "name":"Gennady Korotkevich", 7 | "handle":"tourist", 8 | "photoUrl":"https://userpic.codeforces.org/422/title/50a270ed4a722867.jpg", 9 | "rating": 3600 10 | } 11 | }, 12 | { 13 | "model":"codeforces.user", 14 | "pk":2, 15 | "fields":{ 16 | "name":"Gennady", 17 | "handle":"kourist", 18 | "photoUrl":"http://test.com/" 19 | } 20 | }, 21 | { 22 | "model":"codeforces.user", 23 | "pk":3, 24 | "fields":{ 25 | "name":"", 26 | "handle":"toureiffel-2018", 27 | "photoUrl":"https://userpic.codeforces.org/1366144/title/17765f50a680f35e.jpg" 28 | } 29 | }, 30 | { 31 | "model":"codeforces.user", 32 | "pk":4, 33 | "fields":{ 34 | "name":"Lite leta hun", 35 | "handle":"toourist_ki_ammi_meri", 36 | "photoUrl":"tps://userpic.codeforces.org/no-title.jpg" 37 | } 38 | }, 39 | { 40 | "model":"codeforces.user", 41 | "pk":5, 42 | "fields":{ 43 | "name":"Tourist ", 44 | "handle":"Tourist_01", 45 | "photoUrl":"https://userpic.codeforces.org/2010226/title/c0bd07cd7ea2f1b9.jpg" 46 | } 47 | }, 48 | { 49 | "model":"codeforces.user", 50 | "pk":6, 51 | "fields":{ 52 | "name":"", 53 | "handle":"touristyyds", 54 | "photoUrl":"https://userpic.codeforces.org/1739944/title/c3a83e1b81677e01.jpg" 55 | } 56 | } 57 | ] -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0002_auto_20201223_1545.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2020-12-23 10:15 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='organization_contest_participation', 15 | name='contest', 16 | ), 17 | migrations.RemoveField( 18 | model_name='organization_contest_participation', 19 | name='organization', 20 | ), 21 | migrations.RemoveField( 22 | model_name='contest', 23 | name='participants', 24 | ), 25 | migrations.RemoveField( 26 | model_name='country', 27 | name='current', 28 | ), 29 | migrations.RemoveField( 30 | model_name='country', 31 | name='total', 32 | ), 33 | migrations.RemoveField( 34 | model_name='organization', 35 | name='current', 36 | ), 37 | migrations.RemoveField( 38 | model_name='organization', 39 | name='total', 40 | ), 41 | migrations.RemoveField( 42 | model_name='user', 43 | name='countryRank', 44 | ), 45 | migrations.RemoveField( 46 | model_name='user', 47 | name='organizationRank', 48 | ), 49 | migrations.RemoveField( 50 | model_name='user', 51 | name='worldRank', 52 | ), 53 | migrations.RemoveField( 54 | model_name='user_contest_rank', 55 | name='countryRank', 56 | ), 57 | migrations.RemoveField( 58 | model_name='user_contest_rank', 59 | name='organizationRank', 60 | ), 61 | migrations.DeleteModel(name='country_contest_participation', ), 62 | migrations.DeleteModel(name='organization_contest_participation', ), 63 | ] 64 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0003_auto_20201224_0950.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2020-12-24 04:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0002_auto_20201223_1545'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='user_contest_rank', 15 | name='worldRank', 16 | field=models.IntegerField(blank=True, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0004_auto_20201224_1007.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2020-12-24 04:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0003_auto_20201224_0950'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='contest', 15 | name='duration', 16 | field=models.IntegerField(blank=True, null=True), 17 | ), 18 | migrations.AlterField( 19 | model_name='contest', 20 | name='startTime', 21 | field=models.IntegerField(blank=True, null=True), 22 | ), 23 | migrations.AlterField( 24 | model_name='user', 25 | name='maxRating', 26 | field=models.IntegerField(blank=True, null=True), 27 | ), 28 | migrations.AlterField( 29 | model_name='user', 30 | name='rating', 31 | field=models.IntegerField(blank=True, null=True), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0005_contest_isupdated.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-05 14:55 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0004_auto_20201224_1007'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='contest', 15 | name='isUpdated', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0006_auto_20210106_1021.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-06 04:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0005_contest_isupdated'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='user', 15 | name='handle', 16 | field=models.CharField(max_length=50, unique=True), 17 | ), 18 | migrations.AlterField( 19 | model_name='user', 20 | name='maxRank', 21 | field=models.CharField(blank=True, max_length=50, null=True), 22 | ), 23 | migrations.AlterField( 24 | model_name='user', 25 | name='photoUrl', 26 | field=models.CharField(blank=True, max_length=100, null=True), 27 | ), 28 | migrations.AlterField( 29 | model_name='user', 30 | name='rank', 31 | field=models.CharField(blank=True, max_length=50, null=True), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0007_auto_20210106_1047.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-06 05:17 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0006_auto_20210106_1021'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='user', 15 | name='handle', 16 | field=models.CharField(db_index=True, max_length=50, unique=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0008_auto_20210129_2145.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-29 16:15 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0007_auto_20210106_1047'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='contest', 15 | name='contestId', 16 | field=models.CharField(db_index=True, max_length=10), 17 | ), 18 | migrations.AlterField( 19 | model_name='country', 20 | name='name', 21 | field=models.CharField(blank=True, 22 | db_index=True, 23 | max_length=50, 24 | null=True), 25 | ), 26 | migrations.AlterField( 27 | model_name='organization', 28 | name='name', 29 | field=models.CharField(blank=True, 30 | db_index=True, 31 | max_length=50, 32 | null=True), 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0009_auto_20210929_1435.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-09-29 09:05 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeforces', '0008_auto_20210129_2145'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='user', 15 | name='maxRank', 16 | field=models.CharField(blank=True, 17 | default='UnRated', 18 | max_length=50, 19 | null=True), 20 | ), 21 | migrations.AlterField( 22 | model_name='user', 23 | name='maxRating', 24 | field=models.IntegerField(blank=True, default=0, null=True), 25 | ), 26 | migrations.AlterField( 27 | model_name='user', 28 | name='rank', 29 | field=models.CharField(blank=True, 30 | default='UnRated', 31 | max_length=50, 32 | null=True), 33 | ), 34 | migrations.AlterField( 35 | model_name='user', 36 | name='rating', 37 | field=models.IntegerField(blank=True, default=0, null=True), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/0010_codeforcesproblemset.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-11-16 04:40 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('problem', '0005_auto_20210129_2145'), 11 | ('codeforces', '0009_auto_20210929_1435'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='CodeforcesProblemSet', 17 | fields=[ 18 | ('id', 19 | models.AutoField(auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name='ID')), 23 | ('child', 24 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 25 | related_name='child_problem', 26 | to='problem.problem')), 27 | ('parent', 28 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 29 | related_name='parent_problem', 30 | to='problem.problem')), 31 | ], 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /codedigger/codeforces/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codeforces/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/codeforces/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from problem.models import Problem 3 | 4 | 5 | class organization(models.Model): 6 | name = models.CharField(max_length=50, 7 | blank=True, 8 | null=True, 9 | db_index=True) 10 | 11 | def __str__(self): 12 | return self.name 13 | 14 | 15 | class country(models.Model): 16 | name = models.CharField(max_length=50, 17 | blank=True, 18 | null=True, 19 | db_index=True) 20 | 21 | def __str__(self): 22 | return self.name 23 | 24 | 25 | class contest(models.Model): 26 | TYPE = (('R', 'Regular'), ('G', 'Gym')) 27 | name = models.CharField(max_length=200) 28 | contestId = models.CharField(max_length=10, db_index=True) 29 | duration = models.IntegerField(blank=True, null=True) 30 | startTime = models.IntegerField(blank=True, null=True) 31 | Type = models.CharField(max_length=1, choices=TYPE) 32 | isUpdated = models.BooleanField(default=False) 33 | 34 | def __str__(self): 35 | return self.contestId 36 | 37 | 38 | class user(models.Model): 39 | name = models.CharField( 40 | max_length=100, 41 | blank=True, 42 | null=True, 43 | ) 44 | handle = models.CharField(max_length=50, unique=True, db_index=True) 45 | rating = models.IntegerField(blank=True, null=True, default=0) 46 | maxRating = models.IntegerField(blank=True, null=True, default=0) 47 | rank = models.CharField(max_length=50, 48 | blank=True, 49 | null=True, 50 | default='UnRated') 51 | maxRank = models.CharField(max_length=50, 52 | blank=True, 53 | null=True, 54 | default='UnRated') 55 | country = models.ForeignKey( 56 | country, 57 | on_delete=models.SET_NULL, 58 | blank=True, 59 | null=True, 60 | ) 61 | organization = models.ForeignKey( 62 | organization, 63 | on_delete=models.SET_NULL, 64 | blank=True, 65 | null=True, 66 | ) 67 | photoUrl = models.CharField(max_length=100, blank=True, null=True) 68 | contestRank = models.ManyToManyField(contest, 69 | through='user_contest_rank', 70 | through_fields=('user', 'contest')) 71 | 72 | def __str__(self): 73 | return self.handle 74 | 75 | 76 | class user_contest_rank(models.Model): 77 | user = models.ForeignKey(user, on_delete=models.CASCADE) 78 | contest = models.ForeignKey(contest, on_delete=models.CASCADE) 79 | worldRank = models.IntegerField(blank=True, null=True) 80 | 81 | def __str__(self): 82 | return str(self.user.handle) + ' is participated in ' + str( 83 | self.contest.contestId) 84 | 85 | 86 | class CodeforcesProblemSet(models.Model): 87 | parent = models.ForeignKey(Problem, 88 | related_name='parent_problem', 89 | on_delete=models.CASCADE) 90 | child = models.ForeignKey(Problem, 91 | related_name='child_problem', 92 | on_delete=models.CASCADE) 93 | 94 | def __str__(self): 95 | return '{} is same problem as {}'.format(self.child.prob_id, 96 | self.parent.prob_id) 97 | -------------------------------------------------------------------------------- /codedigger/codeforces/scraper.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | 4 | 5 | def problem_page(url): 6 | res = requests.get(url) 7 | soup = BeautifulSoup(res.content, 'html5lib') 8 | return soup 9 | -------------------------------------------------------------------------------- /codedigger/codeforces/scraper_utils.py: -------------------------------------------------------------------------------- 1 | from .scraper import problem_page 2 | 3 | 4 | def problem_page_text(url): 5 | soup = problem_page(url) 6 | return soup.find_all('div', {'class': 'problem-statement'})[0]\ 7 | .find_all('div', {'class' : ''})[0].text 8 | 9 | 10 | def isSameProblem(url1, url2): 11 | try: 12 | text1 = problem_page_text(url1) 13 | text2 = problem_page_text(url2) 14 | except: 15 | return False 16 | return text1 == text2 17 | -------------------------------------------------------------------------------- /codedigger/codeforces/test_fixtures/models_utils_fixture.py: -------------------------------------------------------------------------------- 1 | cf_user = { 2 | "lastName": "Singhal", 3 | "country": "India", 4 | "lastOnlineTimeSeconds": 1632891493, 5 | "city": "New Delhi", 6 | "rating": 1783, 7 | "friendOfCount": 322, 8 | "titlePhoto": 9 | "https://userpic.codeforces.org/876150/title/f6dbeb71b3f2f7fa.jpg", 10 | "handle": "shivamsinghal1012", 11 | "avatar": 12 | "https://userpic.codeforces.org/876150/avatar/81895ac4772f2c3b.jpg", 13 | "firstName": "Shivam", 14 | "contribution": 21, 15 | "organization": "Delhi Technological University", 16 | "rank": "expert", 17 | "maxRating": 1975, 18 | "registrationTimeSeconds": 1546593498, 19 | "email": "shivamsinghal1012@gmail.com", 20 | "maxRank": "candidate master", 21 | } 22 | 23 | cf_contest = { 24 | "id": 1540, 25 | "name": "Codeforces Round #728 (Div. 1)", 26 | "type": "CF", 27 | "phase": "FINISHED", 28 | "frozen": False, 29 | "durationSeconds": 8100, 30 | "startTimeSeconds": 1624635300, 31 | "relativeTimeSeconds": 8272530, 32 | } 33 | 34 | cf_problem = { 35 | "contestId": 1579, 36 | "index": "C", 37 | "name": "Ticks", 38 | "type": "PROGRAMMING", 39 | "rating": 1500, 40 | "tags": ["greedy", "implementation"], 41 | } 42 | 43 | rating_change_data = [{ 44 | "contestId": 1540, 45 | "contestName": "Codeforces Round #728 (Div. 1)", 46 | "handle": "shivamsinghal1012", 47 | "rank": 1, 48 | "ratingUpdateTimeSeconds": 1624643400, 49 | "oldRating": 3446, 50 | "newRating": 3606, 51 | }, { 52 | "contestId": 1540, 53 | "contestName": "Codeforces Round #728 (Div. 1)", 54 | "handle": "tourist", 55 | "rank": 2, 56 | "ratingUpdateTimeSeconds": 1624643400, 57 | "oldRating": 3723, 58 | "newRating": 3748, 59 | }] 60 | -------------------------------------------------------------------------------- /codedigger/codeforces/test_fixtures/rating_change_fixture.py: -------------------------------------------------------------------------------- 1 | cfcontest = { 2 | 'name': "Codeforces Round #728 (Div. 1)", 3 | 'contestId': 1540, 4 | 'duration': 8100, 5 | 'startTime': 1624635300, 6 | 'Type': 'R', 7 | 'participants': 1500 8 | } 9 | 10 | contest_rank = { 11 | 'contest': cfcontest, 12 | 'worldRank': 20, 13 | 'countryRank': 2, 14 | 'organizationRank': 1, 15 | 'totalCountryParticipants': 100, 16 | 'totalOrganizationParticipants': 20, 17 | } 18 | 19 | rating_change = { 20 | "contestId": 1540, 21 | "contestName": "Codeforces Round #728 (Div. 1)", 22 | "handle": "shivamsinghal1012", 23 | "rank": 20, 24 | "ratingUpdateTimeSeconds": 1624643400, 25 | "oldRating": 1800, 26 | "newRating": 2000, 27 | } 28 | -------------------------------------------------------------------------------- /codedigger/codeforces/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/codeforces/tests/__init__.py -------------------------------------------------------------------------------- /codedigger/codeforces/tests/test_api.py: -------------------------------------------------------------------------------- 1 | from rest_framework import response 2 | from .test_setup import TestSetUp 3 | from user.exception import ValidationException 4 | from codeforces.api import (user_info, user_rating, contest_list, 5 | contest_standings, contest_ratingChanges, 6 | user_status) 7 | from django.urls import reverse 8 | 9 | 10 | class TestAPI(TestSetUp): 11 | 12 | def test_user_info_api(self): 13 | handles = ['shivamsinghal1012', 'shivam011'] 14 | response = user_info(handles) 15 | self.assertEqual(len(response), 2) 16 | self.assertEqual(response[0]['handle'], 'shivamsinghal1012') 17 | 18 | def test_user_rating_api(self): 19 | handle = 'Fefer_Ivan' 20 | response = user_rating(handle) 21 | self.assertTrue(len(response) > 0) 22 | self.assertEqual(response[0]['handle'], handle) 23 | 24 | def test_contest_list(self): 25 | response = contest_list() 26 | self.assertLess(response[0]['id'], 100001) 27 | response = contest_list(gym=True) 28 | self.assertGreaterEqual(response[0]['id'], 100001) 29 | 30 | def test_contest_standings(self): 31 | response = contest_standings(566, count=5) 32 | self.assertEqual(response['contest']['id'], 566) 33 | self.assertEqual(len(response['problems']), 7) 34 | self.assertEqual(len(response['rows']), 5) 35 | self.assertEqual(response['rows'][0]['party']['participantType'], 36 | 'CONTESTANT') 37 | 38 | def test_contest_ratingChanges(self): 39 | response = contest_ratingChanges(566) 40 | self.assertEqual(response[0]['rank'], 1) 41 | 42 | def test_user_status(self): 43 | handle = 'shivamsinghal1012' 44 | wrong_handle = 'er' 45 | starting_from = 1 46 | count = 10 47 | response = user_status(handle=handle, 48 | starting_from=starting_from, 49 | count=count) 50 | self.assertEqual(len(response[0]), 12) 51 | self.assertEqual(len(response), 10) 52 | self.assertRaises(ValidationException, user_status, wrong_handle) 53 | -------------------------------------------------------------------------------- /codedigger/codeforces/tests/test_models_utils.py: -------------------------------------------------------------------------------- 1 | from .test_setup import TestSetUp 2 | from codeforces.test_fixtures.models_utils_fixture import (cf_user, cf_contest, 3 | cf_problem, 4 | rating_change_data) 5 | from codeforces.models_utils import (create_or_update_user, 6 | create_or_update_contest, 7 | create_or_update_problem, 8 | update_and_save_contest_data) 9 | 10 | from codeforces.models import user, contest, user_contest_rank 11 | from problem.models import Problem 12 | 13 | 14 | class TestViews(TestSetUp): 15 | 16 | def test_create_user(self): 17 | newUser = create_or_update_user(cf_user) 18 | users = user.objects.filter(handle=cf_user['handle']) 19 | self.assertEqual(users.exists(), True) 20 | self.assertEqual(newUser.rating, cf_user['rating']) 21 | self.assertEqual(users[0].organization.name, cf_user['organization']) 22 | 23 | def test_create_contest(self): 24 | newContest = create_or_update_contest(cf_contest) 25 | contests = contest.objects.filter(contestId=cf_contest['id']) 26 | self.assertEqual(contests.exists(), True) 27 | self.assertEqual(newContest.name, cf_contest['name']) 28 | self.assertEqual(contests[0].startTime, cf_contest['startTimeSeconds']) 29 | 30 | def test_create_problem(self): 31 | newProblem = create_or_update_problem(cf_problem) 32 | problems = Problem.objects.filter( 33 | prob_id=str(cf_problem['contestId']) + cf_problem['index']) 34 | self.assertEqual(problems.exists(), True) 35 | self.assertEqual(newProblem.rating, cf_problem['rating']) 36 | self.assertEqual(problems[0].contest_id, str(cf_problem['contestId'])) 37 | 38 | def test_update_contest_rank(self): 39 | newContest = create_or_update_contest(cf_contest) 40 | create_or_update_user(cf_user) 41 | update_and_save_contest_data(rating_change_data, newContest) 42 | 43 | qs = user_contest_rank.objects.filter(contest = newContest)\ 44 | .order_by('worldRank') 45 | self.assertEqual(qs.count(), 2) 46 | self.assertEqual(qs[0].worldRank, 1) 47 | self.assertEqual(qs[0].user.handle, rating_change_data[0]['handle']) 48 | self.assertEqual(qs[1].user.handle, rating_change_data[1]['handle']) 49 | -------------------------------------------------------------------------------- /codedigger/codeforces/tests/test_problems.py: -------------------------------------------------------------------------------- 1 | # from django.test import TestCase 2 | from .test_setup import TestSetUp 3 | from codeforces.contestProblem import AssignCodeforcesProblem 4 | from codeforces.models import user 5 | 6 | 7 | class Test_assign_problem(TestSetUp): 8 | 9 | def test_Assign(self): 10 | test_user = user.objects.get(handle='tourist') 11 | problems = AssignCodeforcesProblem(test_user) 12 | self.assertEqual(len(problems), 5) 13 | -------------------------------------------------------------------------------- /codedigger/codeforces/tests/test_scraper_utils.py: -------------------------------------------------------------------------------- 1 | from .test_setup import TestSetUp 2 | 3 | from problem.models import Problem 4 | from codeforces.scraper_utils import isSameProblem 5 | from codeforces.codeforcesProblemSet import join, get_similar_problems 6 | 7 | 8 | class TestViews(TestSetUp): 9 | 10 | def test_is_same_problem(self): 11 | url1 = "https://codeforces.com/problemset/problem/1355/B" 12 | url2 = "https://codeforces.com/gym/102599/problem/D" 13 | self.assertTrue(isSameProblem(url1, url2)) 14 | 15 | url1 = "https://codeforces.com/contest/1603/problem/A" 16 | url2 = "https://codeforces.com/contest/1604/problem/C" 17 | self.assertTrue(isSameProblem(url1, url2)) 18 | 19 | url1 = "https://codeforces.com/contest/1603/problem/B" 20 | self.assertFalse(isSameProblem(url1, url2)) 21 | 22 | def test_join_problem(self): 23 | probA = Problem.objects.get(prob_id='100001A') 24 | probC = Problem.objects.get(prob_id='1549C') 25 | probD = Problem.objects.get(prob_id='1549D') 26 | probE = Problem.objects.get(prob_id='1549E') 27 | 28 | join(probA, probC) 29 | join(probD, probE) 30 | join(probA, probE) 31 | 32 | self.assertEqual(get_similar_problems(probC).count(), 3) 33 | -------------------------------------------------------------------------------- /codedigger/codeforces/tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APITestCase 2 | from rest_framework.test import APIClient 3 | from django.urls import reverse 4 | 5 | from user.models import User, Profile 6 | from lists.test_fixtures.profile_fixtures import profile1, profile2 7 | 8 | 9 | class TestSetUp(APITestCase): 10 | 11 | fixtures = [ 12 | "user.json", "cf_contest.json", "cf_problems.json", "cf_users.json" 13 | ] 14 | 15 | @classmethod 16 | def setUpTestData(cls): 17 | Profile.objects.filter(owner=1).update(**profile1) 18 | Profile.objects.filter(owner=2).update(**profile2) 19 | 20 | def setUp(self): 21 | self.login_url = reverse('login') 22 | self.user_data = {'username': 'testing', 'password': 'QWERTY@123'} 23 | return super().setUp() 24 | 25 | @classmethod 26 | def login(self, client, login_url, user_data): 27 | user = User.objects.get(username=user_data['username']) 28 | user.set_password(user_data['password']) 29 | user.save() 30 | response = client.post(login_url, user_data, format="json") 31 | return response.data['tokens']['access'] 32 | 33 | @classmethod 34 | def get_authenticated_client(self, token): 35 | client = APIClient() 36 | client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) 37 | return client 38 | 39 | def tearDown(self): 40 | return super().tearDown() 41 | -------------------------------------------------------------------------------- /codedigger/codeforces/tests/test_upsolve.py: -------------------------------------------------------------------------------- 1 | from django import test 2 | from .test_setup import TestSetUp 3 | from django.urls import reverse 4 | 5 | 6 | class TestUpsolve(TestSetUp): 7 | 8 | def test_codeforces_upsolve(self): 9 | test_url = reverse('cf_upsolve') 10 | token = self.login(self.client, self.login_url, self.user_data) 11 | client = self.get_authenticated_client(token) 12 | res = client.get(test_url, format="json") 13 | self.assertEqual(res.data['meta']['total'], 1) 14 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 15 | 16 | def test_codeforces_virtual_upsolve(self): 17 | test_url = reverse('cf_upsolve') + '?virtual=true' 18 | token = self.login(self.client, self.login_url, self.user_data) 19 | client = self.get_authenticated_client(token) 20 | res = client.get(test_url, format="json") 21 | self.assertEqual(res.data['meta']['total'], 2) 22 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 23 | 24 | def test_codeforces_without_auth_upsolve(self): 25 | test_url = reverse('cf_upsolve') + '?handle=aaradhya0707' 26 | res = self.client.get(test_url, format="json") 27 | self.assertEqual(res.data['meta']['total'], 1) 28 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 29 | -------------------------------------------------------------------------------- /codedigger/codeforces/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import response 2 | from .test_setup import TestSetUp 3 | from user.exception import ValidationException 4 | from codeforces.api import (user_info, user_rating, contest_list, 5 | contest_standings, contest_ratingChanges, 6 | user_status) 7 | from django.urls import reverse 8 | 9 | 10 | class TestViews(TestSetUp): 11 | 12 | def test_search_user(self): 13 | url = reverse('search-user') 14 | url += '?q=tou' 15 | response = self.client.get(url, format='json') 16 | self.assertEqual(response.status_code, 200) 17 | self.assertEqual(max(len(response.data['result']), 5), 5) 18 | users = response.data['result'] 19 | for i in users: 20 | handle = i['handle'].lower() 21 | self.assertEqual('tou', handle[:3]) 22 | -------------------------------------------------------------------------------- /codedigger/codeforces/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('users', views.SearchUser.as_view(), name="search-user"), 7 | path('mentor', views.MentorAPIView.as_view(), name='mentor'), 8 | path('upsolve', 9 | views.CodeforcesUpsolveAPIView.as_view(), 10 | name="cf_upsolve"), 11 | path('testing', views.testing), 12 | path('rating-change-email', views.rating_change_email) 13 | ] 14 | -------------------------------------------------------------------------------- /codedigger/contest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/contest/__init__.py -------------------------------------------------------------------------------- /codedigger/contest/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from .models import * 5 | 6 | admin.site.register(CodeforcesContest) 7 | admin.site.register(CodeforcesContestProblem) 8 | 9 | # admin.site.register(Contest) 10 | # admin.site.register(ContestProblem) 11 | # admin.site.register(ContestParticipation) 12 | # admin.site.register(ContestResult) 13 | # admin.site.register(CodeforcesContest) 14 | # admin.site.register(CodeforcesContestParticipation) 15 | # admin.site.register(CodeforcesContestSubmission) -------------------------------------------------------------------------------- /codedigger/contest/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ContestConfig(AppConfig): 5 | name = 'contest' 6 | -------------------------------------------------------------------------------- /codedigger/contest/cron.py: -------------------------------------------------------------------------------- 1 | # Cron Job - 2 | # Problem Assign -- Contest with isProblem False -- Assign Problem 3 | # Result Assign -- Contest with isResult False 4 | # contest end -- (startTime + duration) <= time.now 5 | 6 | #Email 7 | from django.core.mail import send_mail 8 | from codedigger.settings import EMAIL_HOST_USER 9 | 10 | ## Short Code Contest 11 | # from .utils import login, clean, penalty 12 | # from .models import CodeforcesContest, CodeforcesContestSubmission, CodeforcesContestParticipation 13 | # import requests, random, re 14 | # from codeforces.cron import save_user 15 | # from codeforces.models import user as CodeforcesUser 16 | # from bs4 import BeautifulSoup as bs 17 | 18 | # def update_penalty(contest, cookie) : 19 | # contestId = contest.contestId 20 | # groupId = contest.groupId 21 | # page = 0 22 | # prevHandle = None 23 | # while(page < 100): 24 | # page+=1 25 | # url = "https://codeforces.com/group/"+groupId+"/contest/"+str(contestId)+"/standings/page/"+str(page) 26 | # res = requests.get(url , headers = {'Cookie' : cookie}) 27 | # soup = bs(res.content,features="html5lib") 28 | # participants = soup.find('table' , {'class' :'standings'}).findAll('tr') 29 | # NProblems = len(participants[0].findAll('th'))-4 30 | # isBreak = False 31 | # isFirst = True 32 | 33 | # for participant in participants[1:-1] : 34 | # column = participant.findAll('td') 35 | # user_handle = clean(column[1].find('a').text) 36 | # if isFirst: 37 | # if user_handle == prevHandle: 38 | # isBreak = True 39 | # break 40 | # else : 41 | # prevHandle = user_handle 42 | # isFirst = False 43 | # contest_user,created = CodeforcesUser.objects.get_or_create(handle = user_handle) 44 | # if created : 45 | # url = "https://codeforces.com/api/user.info?handles="+user_handle 46 | # res = requests.get(url) 47 | # if res.status_code == 200: 48 | # data = res.json() 49 | # if data['status'] == 'OK': 50 | # save_user(contest_user , data['result'][0]) 51 | 52 | # contest_participant,created = CodeforcesContestParticipation.objects.get_or_create( 53 | # contest=contest, 54 | # user=contest_user, 55 | # participantId=participant['participantid'], 56 | # defaults={ 57 | # 'isOfficial' : clean(column[0].text) != '', 58 | # 'isVirtual' : column[1].find('sup') != None 59 | # }) 60 | 61 | # for i in range(NProblems): 62 | 63 | # sub = CodeforcesContestSubmission.objects.filter(participant=contest_participant, problemIndex = i) 64 | 65 | # newSub = CodeforcesContestSubmission(participant=contest_participant, problemIndex = i) 66 | 67 | # if column[4+i].find('span' , {'class' : 'cell-accepted'})!=None and column[4+i]['title'][:3]=='GNU': 68 | # subId = participant.findAll('td')[4+i]['acceptedsubmissionid'] 69 | 70 | # if sub.exists() and str(sub[0].submissionId) == subId : 71 | # continue 72 | 73 | # if sub.exists() : 74 | # sub[0].isSolved = True 75 | # sub[0].submissionId = subId 76 | # sub[0].lang = column[4+i]['title'] 77 | # sub[0].penalty = penalty(cookie, contestId, subId, groupId) 78 | # sub[0].save() 79 | # else : 80 | # newSub.isSolved = True 81 | # newSub.submissionId = subId 82 | # newSub.lang = column[4+i]['title'] 83 | # newSub.penalty = penalty(cookie, contestId, subId, groupId) 84 | # newSub.save() 85 | # else : 86 | # newSub.isSolved = False 87 | # if not sub.exists() : 88 | # newSub.save() 89 | 90 | # if isBreak: 91 | # break 92 | 93 | # def update_codeforces_short_code_contests() : 94 | # cookie = login() 95 | # codeforcescontest = CodeforcesContest.objects.filter(Type = "Short Code") 96 | # for contest in codeforcescontest : 97 | # update_penalty(contest, cookie) 98 | -------------------------------------------------------------------------------- /codedigger/contest/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-11-12 04:18 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('codeforces', '0009_auto_20210929_1435'), 13 | ('problem', '0005_auto_20210129_2145'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='CodeforcesContest', 19 | fields=[ 20 | ('id', 21 | models.AutoField(auto_created=True, 22 | primary_key=True, 23 | serialize=False, 24 | verbose_name='ID')), 25 | ('name', models.CharField(blank=True, 26 | default='', 27 | max_length=63)), 28 | ('duration', models.IntegerField(default=7200)), 29 | ('startTime', models.DateTimeField(auto_now_add=True)), 30 | ('owner', 31 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 32 | related_name='codeforces_contest_user', 33 | to='codeforces.user')), 34 | ], 35 | ), 36 | migrations.CreateModel( 37 | name='CodeforcesContestProblem', 38 | fields=[ 39 | ('id', 40 | models.AutoField(auto_created=True, 41 | primary_key=True, 42 | serialize=False, 43 | verbose_name='ID')), 44 | ('index', models.IntegerField()), 45 | ('codeforcesContest', 46 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 47 | to='contest.codeforcescontest')), 48 | ('problem', 49 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 50 | to='problem.problem')), 51 | ], 52 | ), 53 | migrations.AddField( 54 | model_name='codeforcescontest', 55 | name='problem', 56 | field=models.ManyToManyField( 57 | related_name='codeforces_contest_problem', 58 | through='contest.CodeforcesContestProblem', 59 | to='problem.Problem'), 60 | ), 61 | ] 62 | -------------------------------------------------------------------------------- /codedigger/contest/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/contest/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/contest/model_utils.py: -------------------------------------------------------------------------------- 1 | # Models 2 | from .models import CodeforcesContest, CodeforcesContestProblem 3 | from problem.models import Problem 4 | 5 | 6 | def get_user_contests(user): 7 | return CodeforcesContest.objects.filter(owner = user)\ 8 | .order_by('-startTime') 9 | 10 | 11 | def get_contest_problem(contest): 12 | return CodeforcesContestProblem.objects\ 13 | .filter(codeforcesContest= contest)\ 14 | .order_by('index')\ 15 | .values_list('problem', flat = True) 16 | 17 | 18 | def get_contest_problem_qs(contest): 19 | contest_problem_ids = get_contest_problem(contest) 20 | contest_problem_qs = [] 21 | for id in contest_problem_ids: 22 | contest_problem_qs.append(Problem.objects.get(id=id)) 23 | return contest_problem_qs 24 | 25 | 26 | def put_contest_problem(contest, problems): 27 | for idx, problem in enumerate(problems): 28 | CodeforcesContestProblem(codeforcesContest=contest, 29 | problem=problem, 30 | index=idx).save() 31 | -------------------------------------------------------------------------------- /codedigger/contest/resultMaker.py: -------------------------------------------------------------------------------- 1 | # makeResult of a particular contest 2 | # from .models import Contest, ContestProblem, ContestParticipation, ContestResult 3 | from problem.models import Problem 4 | from user.models import Profile 5 | from datetime import datetime, timedelta 6 | import requests 7 | import pytz 8 | 9 | # def get_problemSubmission(participant_handle, prob_id, startTime, endTime): 10 | 11 | # problemSubmission = {} 12 | # for prob in prob_id: 13 | # problemSubmission[prob] = None 14 | 15 | # res = requests.get("https://codeforces.com/api/user.status?handle=" + 16 | # participant_handle) 17 | # if res.status_code != 200: 18 | # return problemSubmission 19 | # res = res.json() 20 | # if res['status'] != "OK": 21 | # return problemSubmission 22 | 23 | # for submission in res["result"]: 24 | # if 'contestId' in submission['problem'] and 'verdict' in submission: 25 | # problem_id = str(submission["problem"] 26 | # ['contestId']) + submission["problem"]['index'] 27 | # creationTime = submission["creationTimeSeconds"] 28 | # submissionTime = datetime.utcfromtimestamp(creationTime).replace( 29 | # tzinfo=pytz.UTC) 30 | 31 | # if submissionTime < startTime: 32 | # break 33 | 34 | # if submissionTime >= startTime and submissionTime <= endTime: 35 | # if problem_id in prob_id and submission['passedTestCount'] > 0: 36 | # if submission['verdict'] == 'OK' and problemSubmission[ 37 | # problem_id] == None: 38 | # problemSubmission[problem_id] = { 39 | # 'submissionTime': submissionTime - startTime, 40 | # 'penalty': 0 41 | # } 42 | # elif problemSubmission[problem_id] != None: 43 | # problemSubmission[problem_id]['penalty'] += 1 44 | 45 | # return problemSubmission 46 | 47 | # def prepareResult(contest): 48 | 49 | # participants = ContestParticipation.objects.filter(contest=contest) 50 | # problems = ContestProblem.objects.filter(contest=contest) 51 | # prob_id = list(problems.values_list('problem__prob_id', flat=True)) 52 | 53 | # for participant in participants: 54 | 55 | # participant_handle = Profile.objects.get( 56 | # owner=participant.user).codeforces 57 | # problemSubmission = get_problemSubmission( 58 | # participant_handle, prob_id, contest.startTime, 59 | # contest.startTime + contest.duration) 60 | # #print(problemSubmission) 61 | # for problem in problems: 62 | # submission = problemSubmission[problem.problem.prob_id] 63 | # if submission != None: 64 | # cr = ContestResult() 65 | # cr.contestParticipation = participant 66 | # cr.contestProblem = problem 67 | # cr.submissionTime = submission['submissionTime'] 68 | # cr.penalty = submission['penalty'] 69 | # cr.save() 70 | 71 | # contest.isResult = True 72 | # contest.save() 73 | # return 74 | -------------------------------------------------------------------------------- /codedigger/contest/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /codedigger/contest/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.ContestAPIView.as_view(), name='contest.filter'), 7 | # Below Urls are specifically for Codedigger Extension 8 | path('codeforces/', 9 | views.CodeforcesContestAPIView.as_view(), 10 | name='codeforces.contest'), 11 | path('codeforces//', 12 | views.CodeforcesContestGetAPIView.as_view(), 13 | name='codeforces.contest.get'), 14 | path('codeforces//', 15 | views.CodeforcesProblemCheckAPIView.as_view(), 16 | name='codeforces.problem.check') 17 | 18 | # Short Code Contest Url 19 | # path('shortCode',views.ShortCodeContestAPIView.as_view(), name='shortCodeContest'), 20 | # path('shortCode//standing',views.ShortCodeContestStandingAPIView.as_view(), name='Standing.shortCodeContest'), 21 | # path('testing' , views.testing) 22 | ] 23 | -------------------------------------------------------------------------------- /codedigger/contest/utils.py: -------------------------------------------------------------------------------- 1 | import os, requests, random, re 2 | from bs4 import BeautifulSoup as bs 3 | from dotenv import load_dotenv, find_dotenv 4 | 5 | load_dotenv(find_dotenv()) 6 | 7 | 8 | def login(): 9 | # To Login to Codeforces 10 | url = "https://codeforces.com/enter" 11 | res = requests.get(url) 12 | soup = bs(res.content, features="html5lib") 13 | Cookie = res.headers['Set-Cookie'][:47] + res.headers['Set-Cookie'][66:81] 14 | CsrfToken = soup.find('meta', {'name': 'X-Csrf-Token'})['content'] 15 | ftaa = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', 16 | k=18)) 17 | 18 | headers = { 19 | 'Cookie': Cookie, 20 | 'Host': 'codeforces.com', 21 | 'Accept': '*/*', 22 | 'Accept-Encoding': 'gzip, deflate, br', 23 | 'Connection': 'keep-alive', 24 | 'X-Csrf-Token': CsrfToken 25 | } 26 | 27 | body = { 28 | 'csrf_token': CsrfToken, 29 | 'action': 'enter', 30 | 'ftaa': ftaa, 31 | 'bfaa': 'f1b3f18c715565b589b7823cda7448ce', 32 | 'handleOrEmail': os.getenv('CODEFORCES_HANDLE'), 33 | 'password': os.getenv('CODEFORCES_PASSWORD'), 34 | '_tta': '176', 35 | 'remember': 'on' 36 | } 37 | 38 | res = requests.post(url, headers=headers, data=body) 39 | Cookie = res.headers['Set-Cookie'][:47] + res.headers['Set-Cookie'][ 40 | 66:81] + res.headers['Set-Cookie'][146:199] 41 | return Cookie 42 | 43 | 44 | def clean(s): 45 | # Remove Space Tab Newline 46 | return re.sub(r"[\n\t\s\r]*", "", s) 47 | 48 | 49 | def penalty(cookie, contestId, subId, groupId): 50 | # Short Code Contest Penalty 51 | url = "https://codeforces.com/group/" + str(groupId) + "/contest/" + str( 52 | contestId) + "/submission/" + str(subId) 53 | res = requests.get(url, headers={'Cookie': cookie}) 54 | soup = bs(res.content, features="html5lib") 55 | return len(clean(soup.find('pre', {'id': 'program-source-text'}).text)) 56 | -------------------------------------------------------------------------------- /codedigger/lists/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/lists/__init__.py -------------------------------------------------------------------------------- /codedigger/lists/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import List, ListInfo, Solved, ListExtraInfo, LadderStarted, Enrolled 3 | 4 | 5 | class ListInfoAdmin(admin.ModelAdmin): 6 | search_fields = ('p_list__name', 'problem__name', 'problem__prob_id') 7 | 8 | 9 | class ListAdmin(admin.ModelAdmin): 10 | search_fields = ('owner__username', 'name', 'type_list', 'public') 11 | 12 | 13 | class SolvedAdmin(admin.ModelAdmin): 14 | search_fields = ('user__username', 'problem__prob_id', 'problem__name') 15 | 16 | 17 | class ListExtraInfoAdmin(admin.ModelAdmin): 18 | search_fields = ('curr_list__name', ) 19 | 20 | 21 | class LadderStartedAdmin(admin.ModelAdmin): 22 | search_fields = ('ladder_user__name', 'user__username') 23 | 24 | 25 | class EnrolledAdmin(admin.ModelAdmin): 26 | search_fields = ('enroll__user', 'enroll__list') 27 | 28 | 29 | admin.site.register(List, ListAdmin) 30 | admin.site.register(ListInfo, ListInfoAdmin) 31 | admin.site.register(Solved, SolvedAdmin) 32 | admin.site.register(ListExtraInfo, ListExtraInfoAdmin) 33 | admin.site.register(LadderStarted, LadderStartedAdmin) 34 | admin.site.register(Enrolled, EnrolledAdmin) 35 | -------------------------------------------------------------------------------- /codedigger/lists/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ListsConfig(AppConfig): 5 | name = 'lists' 6 | -------------------------------------------------------------------------------- /codedigger/lists/fixtures/enrolled.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "lists.enrolled", 4 | "pk": 1, 5 | "fields": { 6 | "enroll_user": 1, 7 | "enroll_list": 2 8 | } 9 | } 10 | ] -------------------------------------------------------------------------------- /codedigger/lists/fixtures/list_info.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "lists.listinfo", 4 | "pk": 1, 5 | "fields": { 6 | "p_list": 1, 7 | "problem": 1 8 | } 9 | }, 10 | { 11 | "model": "lists.listinfo", 12 | "pk": 2, 13 | "fields": { 14 | "p_list": 1, 15 | "problem": 2 16 | } 17 | }, 18 | { 19 | "model": "lists.listinfo", 20 | "pk": 3, 21 | "fields": { 22 | "p_list": 1, 23 | "problem": 3 24 | } 25 | }, 26 | { 27 | "model": "lists.listinfo", 28 | "pk": 4, 29 | "fields": { 30 | "p_list": 1, 31 | "problem": 4 32 | } 33 | }, 34 | { 35 | "model": "lists.listinfo", 36 | "pk": 5, 37 | "fields": { 38 | "p_list": 1, 39 | "problem": 5 40 | } 41 | }, 42 | { 43 | "model": "lists.listinfo", 44 | "pk": 6, 45 | "fields": { 46 | "p_list": 1, 47 | "problem": 6 48 | } 49 | }, 50 | { 51 | "model": "lists.listinfo", 52 | "pk": 7, 53 | "fields": { 54 | "p_list": 2, 55 | "problem": 1 56 | } 57 | }, 58 | { 59 | "model": "lists.listinfo", 60 | "pk": 8, 61 | "fields": { 62 | "p_list": 2, 63 | "problem": 2 64 | } 65 | }, 66 | { 67 | "model": "lists.listinfo", 68 | "pk": 9, 69 | "fields": { 70 | "p_list": 2, 71 | "problem": 3 72 | } 73 | }, 74 | { 75 | "model": "lists.listinfo", 76 | "pk": 10, 77 | "fields": { 78 | "p_list": 2, 79 | "problem": 4 80 | } 81 | }, 82 | { 83 | "model": "lists.listinfo", 84 | "pk": 11, 85 | "fields": { 86 | "p_list": 2, 87 | "problem": 5 88 | } 89 | }, 90 | { 91 | "model": "lists.listinfo", 92 | "pk": 12, 93 | "fields": { 94 | "p_list": 2, 95 | "problem": 6 96 | } 97 | }, 98 | { 99 | "model": "lists.listinfo", 100 | "pk": 13, 101 | "fields": { 102 | "p_list": 3, 103 | "problem": 6 104 | } 105 | } 106 | ] 107 | -------------------------------------------------------------------------------- /codedigger/lists/fixtures/lists.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "lists.list", 4 | "pk": 1, 5 | "fields": { 6 | "name": "testinglist_topicwise", 7 | "owner": 1, 8 | "isAdmin": true, 9 | "isTopicWise": true, 10 | "type_list": "3", 11 | "public": true, 12 | "slug": "testinglist_topicwise" 13 | } 14 | }, 15 | { 16 | "model": "lists.list", 17 | "pk": 2, 18 | "fields": { 19 | "name": "testinglist_levelwise", 20 | "owner": 1, 21 | "isAdmin": true, 22 | "isTopicWise": false, 23 | "type_list": "3", 24 | "public": true, 25 | "slug": "testinglist_levelwise" 26 | } 27 | }, 28 | { 29 | "model": "lists.list", 30 | "pk": 3, 31 | "fields": { 32 | "name": "testinglist_userlist", 33 | "owner": 1, 34 | "isAdmin": false, 35 | "isTopicWise": true, 36 | "type_list": "3", 37 | "public": false, 38 | "slug": "testinglist_userlist" 39 | } 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /codedigger/lists/fixtures/problems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "problem.problem", 4 | "pk": 1, 5 | "fields": { 6 | "name": "Watermelon", 7 | "prob_id": "4A", 8 | "url": "https://codeforces.com/contest/4/problem/A", 9 | "contest_id": "4", 10 | "rating": 800, 11 | "index": "A", 12 | "tags": "['brute force', 'math']", 13 | "platform": "F", 14 | "difficulty": "B", 15 | "editorial": null 16 | } 17 | }, 18 | { 19 | "model": "problem.problem", 20 | "pk": 2, 21 | "fields": { 22 | "name": "A. Brick", 23 | "prob_id": "abc186_a", 24 | "url": "https://atcoder.jp/contests/abc186/tasks/abc186_a", 25 | "contest_id": "abc186", 26 | "rating": 847, 27 | "index": "a", 28 | "tags": null, 29 | "platform": "A", 30 | "difficulty": "B", 31 | "editorial": null 32 | } 33 | }, 34 | { 35 | "model": "problem.problem", 36 | "pk": 3, 37 | "fields": { 38 | "name": "Relational Operator", 39 | "prob_id": "2113", 40 | "url": "https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=2113", 41 | "contest_id": null, 42 | "rating": 878, 43 | "index": "11172", 44 | "tags": "['Introduction', 'Getting Started: The Easy Problems', 'Super Easy']", 45 | "platform": "U", 46 | "difficulty": "B", 47 | "editorial": null 48 | } 49 | }, 50 | { 51 | "model": "problem.problem", 52 | "pk": 4, 53 | "fields": { 54 | "name": "Add Two Numbers", 55 | "prob_id": "FLOW001", 56 | "url": "https://www.codechef.com/problems/FLOW001", 57 | "contest_id": "", 58 | "rating": 1082, 59 | "index": "", 60 | "tags": "[]", 61 | "platform": "C", 62 | "difficulty": "B", 63 | "editorial": null 64 | } 65 | }, 66 | { 67 | "model": "problem.problem", 68 | "pk": 5, 69 | "fields": { 70 | "name": "A. Simple Math", 71 | "prob_id": "arc107_a", 72 | "url": "https://atcoder.jp/contests/arc107/tasks/arc107_a", 73 | "contest_id": "arc107", 74 | "rating": 1190, 75 | "index": "a", 76 | "tags": null, 77 | "platform": "A", 78 | "difficulty": "E", 79 | "editorial": null 80 | } 81 | }, 82 | { 83 | "model": "problem.problem", 84 | "pk": 6, 85 | "fields": { 86 | "name": "Combination Lock", 87 | "prob_id": "1491", 88 | "url": "https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=1491", 89 | "contest_id": null, 90 | "rating": 1231, 91 | "index": "10550", 92 | "tags": "['Introduction', 'Getting Started: The Easy Problems', 'Super Easy']", 93 | "platform": "U", 94 | "difficulty": "E", 95 | "editorial": null 96 | } 97 | } 98 | ] 99 | -------------------------------------------------------------------------------- /codedigger/lists/fixtures/profiles.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "user.profile", 4 | "pk": 1, 5 | "fields": { 6 | "owner": 1, 7 | "name": "testing", 8 | "codeforces": "aaradhya0707", 9 | "codechef": "aaradhya0707", 10 | "atcoder": "aaradhya0707", 11 | "uva_handle": "aaradhya0707", 12 | "spoj": "aaradhya777", 13 | "uva_id": "1143870" 14 | } 15 | }, 16 | { 17 | "model": "user.profile", 18 | "pk": 2, 19 | "fields": { 20 | "owner": 2, 21 | "name": "testinguser", 22 | "codeforces": "shivam1012" 23 | } 24 | } 25 | ] 26 | -------------------------------------------------------------------------------- /codedigger/lists/fixtures/solved.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"lists.solved", 4 | "pk":1, 5 | "fields": { 6 | "user":1, 7 | "problem":1 8 | } 9 | }, 10 | { 11 | "model":"lists.solved", 12 | "pk":2, 13 | "fields": { 14 | "user":1, 15 | "problem":2 16 | } 17 | }, 18 | { 19 | "model":"lists.solved", 20 | "pk":3, 21 | "fields": { 22 | "user":2, 23 | "problem":1 24 | } 25 | } 26 | ] -------------------------------------------------------------------------------- /codedigger/lists/fixtures/user.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "user.user", 4 | "pk": 1, 5 | "fields": { 6 | "username": "testing", 7 | "email": "testing@gmail.com", 8 | "password": "QWERTY@123", 9 | "is_verified": true, 10 | "is_active": true, 11 | "is_staff": true, 12 | "auth_provider": "email", 13 | "created_at": "2021-10-1T10:00:00.511Z", 14 | "updated_at": "2021-10-1T10:00:00.511Z" 15 | } 16 | }, 17 | { 18 | "model": "user.user", 19 | "pk": 2, 20 | "fields": { 21 | "username": "testinguser", 22 | "email": "testinguser@gmail.com", 23 | "password": "QWERTY@123", 24 | "is_verified": true, 25 | "is_active": true, 26 | "is_staff": false, 27 | "auth_provider": "email", 28 | "created_at": "2021-10-1T10:00:00.511Z", 29 | "updated_at": "2021-10-1T10:00:00.511Z" 30 | } 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /codedigger/lists/fixtures/userfriends.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"user.userfriends", 4 | "pk": 1, 5 | "fields": { 6 | "id":1, 7 | "status":1, 8 | "from_user_id":1, 9 | "to_user_id":2 10 | } 11 | } 12 | 13 | ] -------------------------------------------------------------------------------- /codedigger/lists/migrations/0002_auto_20210102_1525.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-02 09:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('lists', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='listinfo', 15 | options={}, 16 | ), 17 | migrations.RemoveField( 18 | model_name='listinfo', 19 | name='added_at', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /codedigger/lists/migrations/0003_list_public.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-08 10:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('lists', '0002_auto_20210102_1525'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='list', 15 | name='public', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/lists/migrations/0004_listextrainfo.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-27 07:34 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('lists', '0003_list_public'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='ListExtraInfo', 16 | fields=[ 17 | ('id', 18 | models.AutoField(auto_created=True, 19 | primary_key=True, 20 | serialize=False, 21 | verbose_name='ID')), 22 | ('difficulty', models.IntegerField(blank=True, null=True)), 23 | ('video_link', 24 | models.CharField(blank=True, max_length=200, null=True)), 25 | ('contest_link', 26 | models.CharField(blank=True, max_length=200, null=True)), 27 | ('editorial', 28 | models.CharField(blank=True, max_length=200, null=True)), 29 | ('curr_list', 30 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 31 | related_name='curr_list_extra', 32 | to='lists.list')), 33 | ], 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /codedigger/lists/migrations/0005_ladderstarted.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-02-01 16:18 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('lists', '0004_listextrainfo'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='LadderStarted', 18 | fields=[ 19 | ('id', 20 | models.AutoField(auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name='ID')), 24 | ('ladder', 25 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 26 | related_name='ladder', 27 | to='lists.list')), 28 | ('ladder_user', 29 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 30 | related_name='ladder_user', 31 | to=settings.AUTH_USER_MODEL)), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /codedigger/lists/migrations/0006_enrolled.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-12-11 06:52 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('lists', '0005_ladderstarted'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Enrolled', 18 | fields=[ 19 | ('id', 20 | models.AutoField(auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name='ID')), 24 | ('enroll_list', 25 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 26 | related_name='enroll_user', 27 | to='lists.list')), 28 | ('enroll_user', 29 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 30 | related_name='enroll_user', 31 | to=settings.AUTH_USER_MODEL)), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /codedigger/lists/migrations/0007_auto_20211227_1032.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-12-27 05:02 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('lists', '0006_enrolled'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='enrolled', 16 | name='enroll_list', 17 | field=models.ForeignKey( 18 | on_delete=django.db.models.deletion.CASCADE, 19 | related_name='enroll_list', 20 | to='lists.list'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /codedigger/lists/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/lists/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/lists/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework import permissions 2 | 3 | 4 | class IsOwner(permissions.BasePermission): 5 | 6 | def has_object_permission(self, request, view, obj): 7 | return obj.owner == request.user 8 | -------------------------------------------------------------------------------- /codedigger/lists/test_fixtures/profile_fixtures.py: -------------------------------------------------------------------------------- 1 | profile1 = { 2 | "name": "testing", 3 | "codeforces": "aaradhya0707", 4 | "codechef": "aaradhya0707", 5 | "atcoder": "aaradhya0707", 6 | "uva_handle": "aaradhya0707", 7 | "spoj": "aaradhya777", 8 | "uva_id": "1143870", 9 | "gurus": ",shivamsinghal1012," 10 | } 11 | 12 | profile2 = { 13 | "name": "testinguser", 14 | "codeforces": "shivamsinghal1012", 15 | "codechef": "shivam1012" 16 | } 17 | -------------------------------------------------------------------------------- /codedigger/lists/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/lists/tests/__init__.py -------------------------------------------------------------------------------- /codedigger/lists/tests/test_model.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/lists/tests/test_model.py -------------------------------------------------------------------------------- /codedigger/lists/tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APITestCase, APIClient 2 | from django.urls import reverse 3 | #from faker import Faker 4 | 5 | from user.models import User, Profile 6 | from lists.models import List, Solved, ListInfo, Enrolled 7 | from problem.models import Problem 8 | 9 | from lists.test_fixtures.profile_fixtures import profile1, profile2 10 | 11 | 12 | class TestSetUp(APITestCase): 13 | fixtures = [ 14 | "user.json", "problems.json", "lists.json", "list_info.json", 15 | "enrolled.json", "solved.json", "userfriends.json" 16 | ] 17 | 18 | @classmethod 19 | def setUpTestData(cls): 20 | # Set up data for the whole TestCase 21 | Profile.objects.filter(owner=1).update(**profile1) 22 | Profile.objects.filter(owner=2).update(**profile2) 23 | 24 | @classmethod 25 | def login(self, client, login_url, user_data): 26 | user = User.objects.get(username=user_data['username']) 27 | user.set_password(user_data['password']) 28 | user.save() 29 | response = client.post(login_url, user_data, format="json") 30 | return response.data['tokens']['access'] 31 | 32 | @classmethod 33 | def get_authenticated_client(self, token): 34 | client = APIClient() 35 | client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) 36 | return client 37 | 38 | def setUp(self): 39 | self.register_url = reverse('register') 40 | self.login_url = reverse('login') 41 | #self.fake = Faker() 42 | 43 | self.user_data = { 44 | # 'email': 'testing@gmail.com',#self.fake.email(), 45 | 'username': 'testing', #self.fake.email().split('@')[0], 46 | 'password': 'QWERTY@123' #self.fake.email(), 47 | } 48 | return super().setUp() 49 | 50 | def tearDown(self): 51 | return super().tearDown() 52 | -------------------------------------------------------------------------------- /codedigger/lists/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import ( 3 | TopicwiseGetListView, 4 | TopicWiseRetrieveView, 5 | TopicwiseGetLadderView, 6 | TopicWiseLadderRetrieveView, 7 | LevelwiseGetListView, 8 | LevelwiseRetrieveView, 9 | LevelwiseGetLadderView, 10 | LevelwiseLadderRetrieveView, 11 | #updateLadderview, 12 | #updateListView, 13 | UserlistCreateView, 14 | UserlistGetView, 15 | UserlistAddProblemView, 16 | UserStandingStats, 17 | EditUserlistView, 18 | AddProblemsAdminView, 19 | ProblemsPublicListView, 20 | SearchUserlistView, 21 | ListGetView, 22 | EnrollListView, 23 | testing) 24 | 25 | urlpatterns = [ 26 | path('topicwise/list/', 27 | TopicwiseGetListView.as_view(), 28 | name='topicwise-list'), 29 | path('topicwise/list/', 30 | TopicWiseRetrieveView.as_view(), 31 | name='topicwise-list-name'), 32 | path('topicwise/ladder/', 33 | TopicwiseGetLadderView.as_view(), 34 | name='topicwise-ladder'), 35 | path('topicwise/ladder/', 36 | TopicWiseLadderRetrieveView.as_view(), 37 | name='topicwise-list-name'), 38 | path('levelwise/list/', 39 | LevelwiseGetListView.as_view(), 40 | name='levelwise-list'), 41 | path('levelwise/list/', 42 | LevelwiseRetrieveView.as_view(), 43 | name='levelwise-list-name'), 44 | path('levelwise/ladder/', 45 | LevelwiseGetLadderView.as_view(), 46 | name='levelwise-ladder'), 47 | path('levelwise/ladder/', 48 | LevelwiseLadderRetrieveView.as_view(), 49 | name='levelwise-list-name'), 50 | path('/problems', 51 | ProblemsPublicListView.as_view(), 52 | name='problem-publiclist'), 53 | #path('ladder-update',updateLadderview.as_view(),name='ladder-update'), 54 | #path('list-update',updateListView.as_view(),name='list-update'), 55 | path('add-problems-admin/', 56 | AddProblemsAdminView.as_view(), 57 | name='add-problems-admin'), 58 | path('userlist/', UserlistGetView.as_view(), name='userlist-get'), 59 | path('userlist/new', UserlistCreateView.as_view(), name='userlist-create'), 60 | path('userlist/add', UserlistAddProblemView.as_view(), 61 | name='userlist-add'), 62 | path('userlist/edit/', 63 | EditUserlistView.as_view(), 64 | name='userlist-edit'), 65 | path('enroll-list/', EnrollListView.as_view(), name='enroll-list'), 66 | path('userlists', SearchUserlistView.as_view(), name='userlist-search'), 67 | path('user/', ListGetView.as_view(), name='user-list'), 68 | # path('/stats', ListStats.as_view(), name='list-stats'), 69 | path('/stats/standing', 70 | UserStandingStats.as_view(), 71 | name='user-standing'), 72 | path('testing', testing), 73 | ] 74 | -------------------------------------------------------------------------------- /codedigger/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'codedigger.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?") from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /codedigger/problem/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/problem/__init__.py -------------------------------------------------------------------------------- /codedigger/problem/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Problem 3 | 4 | 5 | class ProblemAdmin(admin.ModelAdmin): 6 | search_fields = ( 7 | 'prob_id', 8 | 'platform', 9 | 'name', 10 | ) 11 | 12 | 13 | admin.site.register(Problem, ProblemAdmin) 14 | -------------------------------------------------------------------------------- /codedigger/problem/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProblemConfig(AppConfig): 5 | name = 'problem' 6 | -------------------------------------------------------------------------------- /codedigger/problem/cron.py: -------------------------------------------------------------------------------- 1 | from .scraper.spoj import scraper 2 | from .scraper.uva import update_uva_problems 3 | from .scraper.atcoder import update_atcoder_problems 4 | from .scraper.codechef import codeChefScraper 5 | 6 | from django.shortcuts import render 7 | from codedigger.settings import EMAIL_HOST_USER 8 | from django.core.mail import send_mail 9 | 10 | 11 | def update_spoj(): 12 | #checking if cronjobs has started , you will get a mail 13 | subject = 'SPOJ Scraping Started' 14 | message = 'Hope you are enjoying our Problems' 15 | recepient = 'testing.codedigger@gmail.com' 16 | send_mail(subject, 17 | message, 18 | EMAIL_HOST_USER, [recepient], 19 | fail_silently=False) 20 | scraper() 21 | subject = 'SPOJ Scraping Finished' 22 | message = 'Hope you are enjoying our Problems' 23 | recepient = 'testing.codedigger@gmail.com' 24 | send_mail(subject, 25 | message, 26 | EMAIL_HOST_USER, [recepient], 27 | fail_silently=False) 28 | 29 | 30 | def update_atcoder(): 31 | subject = 'Atcoder Problem Update Process Started' 32 | message = 'Hope you are enjoying our Problems' 33 | recepient = 'testing.codedigger@gmail.com' 34 | send_mail(subject, 35 | message, 36 | EMAIL_HOST_USER, [recepient], 37 | fail_silently=False) 38 | update_atcoder_problems() 39 | subject = 'Atcoder Problem Update Process Finished' 40 | message = 'Hope you are enjoying our Problems' 41 | recepient = 'testing.codedigger@gmail.com' 42 | send_mail(subject, 43 | message, 44 | EMAIL_HOST_USER, [recepient], 45 | fail_silently=False) 46 | 47 | 48 | def update_uva(): 49 | subject = 'UVa Problem Update Process Started' 50 | message = 'Hope you are enjoying our Problems' 51 | recepient = 'testing.codedigger@gmail.com' 52 | send_mail(subject, 53 | message, 54 | EMAIL_HOST_USER, [recepient], 55 | fail_silently=False) 56 | update_uva_problems() 57 | subject = 'UVa Problem Update Process Finished' 58 | message = 'Hope you are enjoying our Problems' 59 | recepient = 'testing.codedigger@gmail.com' 60 | send_mail(subject, 61 | message, 62 | EMAIL_HOST_USER, [recepient], 63 | fail_silently=False) 64 | 65 | 66 | def update_codechef(): 67 | subject = 'Codechef Update Process Started' 68 | message = 'Hope you are enjoying our Problems' 69 | recepient = 'testing.codedigger@gmail.com' 70 | send_mail(subject, 71 | message, 72 | EMAIL_HOST_USER, [recepient], 73 | fail_silently=False) 74 | codeChefScraper() 75 | subject = 'Codechef Update Process Finished' 76 | message = 'Hope you are enjoying our Problems' 77 | recepient = 'testing.codedigger@gmail.com' 78 | send_mail(subject, 79 | message, 80 | EMAIL_HOST_USER, [recepient], 81 | fail_silently=False) 82 | -------------------------------------------------------------------------------- /codedigger/problem/fixtures/at_problems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"problem.problem", 4 | "pk":16, 5 | "fields":{ 6 | "name":"abc172_a", 7 | "prob_id":"abc172_a", 8 | "url":"https://atcoder.jp/contests/abc172/tasks/abc172_a", 9 | "contest_id":"abc172", 10 | "platform":"A", 11 | "rating": 825, 12 | "index":"a" 13 | } 14 | }, 15 | { 16 | "model":"problem.problem", 17 | "pk":17, 18 | "fields":{ 19 | "name":"abc172_b", 20 | "prob_id":"abc172_b", 21 | "url":"https://atcoder.jp/contests/abc172/tasks/abc172_b", 22 | "contest_id":"abc172", 23 | "platform":"A", 24 | "rating": 1252, 25 | "index":"b" 26 | } 27 | }, 28 | { 29 | "model":"problem.problem", 30 | "pk":18, 31 | "fields":{ 32 | "name":"abc172_c", 33 | "prob_id":"abc172_c", 34 | "url":"https://atcoder.jp/contests/abc172/tasks/abc172_c", 35 | "contest_id":"abc172", 36 | "platform":"A", 37 | "rating": 1645, 38 | "index":"c" 39 | } 40 | }, 41 | { 42 | "model":"problem.problem", 43 | "pk":19, 44 | "fields":{ 45 | "name":"abc172_d", 46 | "prob_id":"abc172_d", 47 | "url":"https://atcoder.jp/contests/abc172/tasks/abc172_d", 48 | "contest_id":"abc172", 49 | "platform":"A", 50 | "rating": 1899, 51 | "index":"d" 52 | } 53 | }, 54 | { 55 | "model":"problem.problem", 56 | "pk":20, 57 | "fields":{ 58 | "name":"abc172_e", 59 | "prob_id":"abc172_e", 60 | "url":"https://atcoder.jp/contests/abc172/tasks/abc172_e", 61 | "contest_id":"abc172", 62 | "platform":"A", 63 | "rating": 2525, 64 | "index":"e" 65 | } 66 | } 67 | ] -------------------------------------------------------------------------------- /codedigger/problem/fixtures/atcoder_contests.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"problem.atcoder_contest", 4 | "pk":1, 5 | "fields":{ 6 | "name":"abc172", 7 | "contestId":"abc172" 8 | } 9 | } 10 | ] -------------------------------------------------------------------------------- /codedigger/problem/fixtures/cc_problems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"problem.problem", 4 | "pk":11, 5 | "fields":{ 6 | "name":"COVIDLQ", 7 | "prob_id":"COVIDLQ", 8 | "url":"https://www.codechef.com/problems/COVIDLQ", 9 | "platform":"C", 10 | "rating": 825, 11 | "difficulty": "B", 12 | "index":"APRIL20B" 13 | } 14 | }, 15 | { 16 | "model":"problem.problem", 17 | "pk":12, 18 | "fields":{ 19 | "name":"CARSELL", 20 | "prob_id":"CARSELL", 21 | "url":"https://www.codechef.com/problems/CARSELL", 22 | "platform":"C", 23 | "rating": 1656, 24 | "difficulty": "H", 25 | "index":"APRIL20B" 26 | } 27 | }, 28 | { 29 | "model":"problem.problem", 30 | "pk":13, 31 | "fields":{ 32 | "name":"STRNO", 33 | "prob_id":"STRNO", 34 | "url":"https://www.codechef.com/problems/STRNO", 35 | "platform":"C", 36 | "rating": 1252, 37 | "difficulty": "E", 38 | "index":"APRIL20B" 39 | } 40 | }, 41 | { 42 | "model":"problem.problem", 43 | "pk":14, 44 | "fields":{ 45 | "name":"UNITGCD", 46 | "prob_id":"UNITGCD", 47 | "url":"https://www.codechef.com/problems/UNITGCD", 48 | "platform":"C", 49 | "rating": 1656, 50 | "index":"APRIL20B" 51 | } 52 | }, 53 | { 54 | "model":"problem.problem", 55 | "pk":15, 56 | "fields":{ 57 | "name":"ANSLEAK", 58 | "prob_id":"ANSLEAK", 59 | "url":"https://www.codechef.com/problems/ANSLEAK", 60 | "platform":"C", 61 | "rating": 2528, 62 | "index":"APRIL20B" 63 | } 64 | }, 65 | { 66 | "model":"problem.problem", 67 | "pk":16, 68 | "fields":{ 69 | "name":"EVENTUAL", 70 | "prob_id":"EVENTUAL", 71 | "url":"https://www.codechef.com/problems/EVENTUAL", 72 | "platform":"C", 73 | "rating": 2528, 74 | "index":"COOK120B" 75 | } 76 | }, 77 | { 78 | "model":"problem.problem", 79 | "pk":17, 80 | "fields":{ 81 | "name":"ORTHODOX", 82 | "prob_id":"ORTHODOX", 83 | "url":"https://www.codechef.com/problems/ORTHODOX", 84 | "platform":"C", 85 | "rating": 2528, 86 | "index":"COOK120B" 87 | } 88 | }, 89 | { 90 | "model":"problem.problem", 91 | "pk":18, 92 | "fields":{ 93 | "name":"XORCIST", 94 | "prob_id":"XORCIST", 95 | "url":"https://www.codechef.com/problems/XORCIST", 96 | "platform":"C", 97 | "rating": 2528, 98 | "index":"COOK120B" 99 | } 100 | } 101 | ] -------------------------------------------------------------------------------- /codedigger/problem/fixtures/cf_solved.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model":"lists.solved", 4 | "pk":1, 5 | "fields":{ 6 | "user":1, 7 | "problem":6 8 | } 9 | }, 10 | { 11 | "model":"lists.solved", 12 | "pk":2, 13 | "fields":{ 14 | "user":1, 15 | "problem":7 16 | } 17 | } 18 | ] -------------------------------------------------------------------------------- /codedigger/problem/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2020-12-18 10:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='atcoder_contest', 15 | fields=[ 16 | ('id', 17 | models.AutoField(auto_created=True, 18 | primary_key=True, 19 | serialize=False, 20 | verbose_name='ID')), 21 | ('contestId', models.CharField(max_length=50)), 22 | ('name', models.CharField(blank=True, 23 | max_length=200, 24 | null=True)), 25 | ('startTime', 26 | models.CharField(blank=True, max_length=20, null=True)), 27 | ('duration', 28 | models.CharField(blank=True, max_length=10, null=True)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name='Problem', 33 | fields=[ 34 | ('id', 35 | models.AutoField(auto_created=True, 36 | primary_key=True, 37 | serialize=False, 38 | verbose_name='ID')), 39 | ('name', models.CharField(blank=True, 40 | max_length=200, 41 | null=True)), 42 | ('prob_id', models.CharField(max_length=50)), 43 | ('url', models.CharField(max_length=200)), 44 | ('tags', models.CharField(blank=True, 45 | max_length=500, 46 | null=True)), 47 | ('contest_id', 48 | models.CharField(blank=True, max_length=50, null=True)), 49 | ('index', models.CharField(blank=True, 50 | max_length=20, 51 | null=True)), 52 | ('rating', 53 | models.CharField(blank=True, max_length=10, null=True)), 54 | ('platform', 55 | models.CharField(choices=[('F', 'Codeforces'), 56 | ('C', 'Codechef'), ('S', 'Spoj'), 57 | ('U', 'Uva'), ('A', 'Atcoder')], 58 | max_length=1)), 59 | ('difficulty', 60 | models.CharField(blank=True, 61 | choices=[('B', 'Beginner'), ('E', 'Easy'), 62 | ('M', 'Medium'), ('H', 'Hard'), 63 | ('S', 'Super-Hard'), 64 | ('C', 'Challenging')], 65 | max_length=1, 66 | null=True)), 67 | ('editorial', 68 | models.CharField(blank=True, max_length=200, null=True)), 69 | ], 70 | options={ 71 | 'ordering': ['name'], 72 | }, 73 | ), 74 | ] 75 | -------------------------------------------------------------------------------- /codedigger/problem/migrations/0002_auto_20210106_1154.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-06 06:24 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('problem', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='problem', 15 | name='rating', 16 | field=models.IntegerField(default=5000), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/problem/migrations/0003_auto_20210106_1158.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-06 06:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('problem', '0002_auto_20210106_1154'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='problem', 15 | name='rating', 16 | field=models.IntegerField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/problem/migrations/0004_auto_20210106_1200.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-06 06:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('problem', '0003_auto_20210106_1158'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='problem', 15 | name='rating', 16 | field=models.IntegerField(blank=True, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/problem/migrations/0005_auto_20210129_2145.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-29 16:15 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('problem', '0004_auto_20210106_1200'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='atcoder_contest', 15 | name='contestId', 16 | field=models.CharField(db_index=True, max_length=50), 17 | ), 18 | migrations.AlterField( 19 | model_name='problem', 20 | name='prob_id', 21 | field=models.CharField(db_index=True, max_length=50), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /codedigger/problem/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/problem/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/problem/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | import random 3 | 4 | PLATFORM = (('F', 'Codeforces'), ('C', 'Codechef'), ('S', 'Spoj'), 5 | ('U', 'Uva'), ('A', 'Atcoder')) 6 | DIFFICULTY = (('B', 'Beginner'), ('E', 'Easy'), ('M', 'Medium'), ('H', 'Hard'), 7 | ('S', 'Super-Hard'), ('C', 'Challenging')) 8 | 9 | 10 | class Problem(models.Model): 11 | name = models.CharField(max_length=200, blank=True, null=True) 12 | prob_id = models.CharField(max_length=50, db_index=True) 13 | url = models.CharField(max_length=200) 14 | tags = models.CharField(max_length=500, blank=True, null=True) 15 | contest_id = models.CharField(max_length=50, blank=True, null=True) 16 | index = models.CharField(max_length=20, blank=True, null=True) 17 | rating = models.IntegerField(null=True, blank=True) 18 | platform = models.CharField(max_length=1, choices=PLATFORM) 19 | difficulty = models.CharField(max_length=1, 20 | choices=DIFFICULTY, 21 | blank=True, 22 | null=True) 23 | editorial = models.CharField(max_length=200, blank=True, null=True) 24 | 25 | class Meta: 26 | ordering = ['name'] 27 | 28 | def __str__(self): 29 | return self.prob_id 30 | 31 | def save(self, **kwargs): 32 | if self.rating is not None: 33 | super(Problem, self).save() 34 | else: 35 | if self.difficulty is None: 36 | self.rating = random.randint(800, 4000) 37 | super(Problem, self).save() 38 | else: 39 | if self.difficulty == 'B': 40 | self.rating = random.randint(800, 1100) 41 | 42 | elif self.difficulty == 'E': 43 | self.rating = random.randint(1100, 1500) 44 | 45 | elif self.difficulty == 'M': 46 | self.rating = random.randint(1500, 1800) 47 | 48 | elif self.difficulty == 'H': 49 | self.rating = random.randint(1800, 2100) 50 | 51 | elif self.difficulty == 'S': 52 | self.rating = random.randint(2100, 2600) 53 | 54 | else: 55 | self.rating = random.randint(2600, 4000) 56 | 57 | super(Problem, self).save() 58 | 59 | 60 | class atcoder_contest(models.Model): 61 | contestId = models.CharField(max_length=50, db_index=True) 62 | name = models.CharField(max_length=200, blank=True, null=True) 63 | startTime = models.CharField(max_length=20, blank=True, null=True) 64 | duration = models.CharField(max_length=10, blank=True, null=True) 65 | -------------------------------------------------------------------------------- /codedigger/problem/scraper/atcoder.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from math import log2 3 | from problem.models import Problem, atcoder_contest 4 | 5 | 6 | def rating_to_difficulty(rating): 7 | if rating <= 1100: 8 | return 'B' 9 | elif rating <= 1500: 10 | return 'E' 11 | elif rating <= 1800: 12 | return 'M' 13 | elif rating <= 2100: 14 | return 'H' 15 | elif rating <= 2600: 16 | return 'S' 17 | else: 18 | return 'C' 19 | 20 | 21 | def update_atcoder_problems(): 22 | url = "https://kenkoooo.com/atcoder/resources/contests.json" 23 | res = requests.get(url) 24 | data = res.json() 25 | 26 | for contest in data: 27 | 28 | if len(atcoder_contest.objects.filter(contestId=contest['id'])) == 0: 29 | 30 | new_contest = atcoder_contest() 31 | new_contest.name = contest['title'] 32 | new_contest.contestId = contest['id'] 33 | new_contest.startTime = contest['start_epoch_second'] 34 | new_contest.duration = contest['duration_second'] 35 | new_contest.save() 36 | 37 | url = "https://kenkoooo.com/atcoder/resources/problems.json" 38 | res = requests.get(url) 39 | data = res.json() 40 | 41 | for prob in data: 42 | 43 | if len(Problem.objects.filter(prob_id=prob['id'], platform='A')) == 0: 44 | 45 | new_problem = Problem() 46 | new_problem.prob_id = prob['id'] 47 | new_problem.contest_id = prob['contest_id'] 48 | new_problem.name = prob['title'] 49 | new_problem.url = "https://atcoder.jp/contests/" + prob[ 50 | 'contest_id'] + "/tasks/" + prob['id'] 51 | new_problem.index = prob['id'].split("_")[-1] 52 | new_problem.platform = 'A' 53 | new_problem.save() 54 | 55 | url = "https://kenkoooo.com/atcoder/resources/problem-models.json" 56 | res = requests.get(url) 57 | data = res.json() 58 | 59 | problems = Problem.objects.filter(difficulty=None, platform='A') 60 | 61 | for prob in problems: 62 | 63 | if prob.prob_id in data: 64 | 65 | if 'difficulty' in data[prob.prob_id]: 66 | 67 | old = data[prob.prob_id]['difficulty'] 68 | if old < -1000: 69 | NewValue = (((old + 10000) * 50) / 9000) + 800 70 | elif old <= 0: 71 | NewValue = (((old + 1000) * 350) / 1000) + 850 72 | else: 73 | NewValue = ((old * 2400) / 5000) + 1200 74 | 75 | prob.rating = str(int(NewValue)) 76 | prob.difficulty = rating_to_difficulty(int(NewValue)) 77 | prob.save() 78 | -------------------------------------------------------------------------------- /codedigger/problem/scraper/taglist.txt: -------------------------------------------------------------------------------- 1 | easy 2 | medium 3 | dynamic-programming 4 | cakewalk 5 | easy-medium 6 | simple 7 | hard 8 | medium-hard 9 | math 10 | ad-hoc 11 | greedy 12 | segment-tree 13 | graphs 14 | binary-search 15 | maths 16 | sorting 17 | combinatorics 18 | implementation 19 | strings 20 | observation 21 | trees 22 | number-theory 23 | challenge 24 | geometry 25 | array 26 | data-structure 27 | prefix-sum 28 | hashing 29 | game-theory 30 | brute-force 31 | bitwise-operation 32 | bit-manipulation 33 | breadth-first-search 34 | constructive 35 | probability 36 | greatest-common-divisor 37 | sieve 38 | fenwick-tree 39 | matrix-exponentiation 40 | encoding 41 | sqrt-decomp 42 | lowest-common-ancestor 43 | disjoint-set-union 44 | bitmasking 45 | two-pointers 46 | recursion 47 | looping 48 | trie 49 | modulo 50 | xor 51 | inclusion-exclusion 52 | expected-value 53 | cook-off 54 | pre-computation 55 | heavy-light-decomposition 56 | convex-hull 57 | simulation 58 | set 59 | lazy-propagation 60 | basic-programming 61 | stack 62 | codechef 63 | algorithm 64 | suffix-array 65 | palindrome 66 | divide-and-conquer 67 | c-plus-plus 68 | shortest-path 69 | prime 70 | matrices 71 | mathematics 72 | dijkstra-algorithm 73 | bipartite 74 | modular-arith 75 | maximum-flow 76 | matching 77 | union-find 78 | permutation 79 | factorization 80 | contest 81 | centroid-decomposition 82 | programming 83 | map 84 | ankushkhanna 85 | standard-template-library 86 | simple-easy 87 | eulerian-tour 88 | persistence 89 | meet-in-middle 90 | may20 91 | knuth-morris-pratt-algorithm 92 | fibonacci-numbers 93 | exponentiation 94 | square-root-decomposition 95 | randomized 96 | prime-factorization 97 | polynomial 98 | med-hard 99 | knapsack 100 | heap 101 | gaussian-elimination 102 | counting-sort 103 | binary 104 | -------------------------------------------------------------------------------- /codedigger/problem/scraper/uva.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from math import log2 3 | from problem.models import Problem 4 | 5 | 6 | def rating_to_difficulty(rating): 7 | if rating <= 1100: 8 | return 'B' 9 | elif rating <= 1500: 10 | return 'E' 11 | elif rating <= 1800: 12 | return 'M' 13 | elif rating <= 2100: 14 | return 'H' 15 | elif rating <= 2600: 16 | return 'S' 17 | else: 18 | return 'C' 19 | 20 | 21 | def update_uva_problems(): 22 | 23 | url = "https://uhunt.onlinejudge.org/api/p" 24 | res = requests.get(url) 25 | data = res.json() 26 | 27 | for p in data: 28 | 29 | if len(Problem.objects.filter(prob_id=p[0])) > 0: 30 | continue 31 | 32 | new_problem = Problem() 33 | new_problem.prob_id = p[0] 34 | new_problem.index = p[1] 35 | new_problem.name = p[2] 36 | dacu = p[3] 37 | wa = p[16] 38 | 39 | if dacu != 0: 40 | rating = min(3600, max(800, (20 - min(log2(dacu), 20)) * 200)) 41 | new_problem.rating = str(int(rating)) 42 | new_problem.difficulty = rating_to_difficulty(rating) 43 | 44 | new_problem.platform = 'U' 45 | new_problem.url = "https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=" + str( 46 | p[0]) 47 | new_problem.save() 48 | 49 | url = "https://uhunt.onlinejudge.org/api/cpbook/3" 50 | res = requests.get(url) 51 | data = res.json() 52 | 53 | for p in data: 54 | title1 = p['title'] 55 | for q in p['arr']: 56 | title2 = q['title'] 57 | for r in q['arr']: 58 | title3 = r[0] 59 | for i in range(1, len(r)): 60 | prob_num = abs(r[i]) 61 | tags = [title1, title2, title3] 62 | Problem.objects.filter(index=prob_num).update(tags=tags) 63 | -------------------------------------------------------------------------------- /codedigger/problem/sender.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/problem/sender.py -------------------------------------------------------------------------------- /codedigger/problem/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/problem/tests/__init__.py -------------------------------------------------------------------------------- /codedigger/problem/tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APITestCase 2 | from rest_framework.test import APIClient 3 | from django.urls import reverse 4 | 5 | from user.models import User, Profile 6 | from lists.test_fixtures.profile_fixtures import profile1, profile2 7 | 8 | 9 | class TestSetUp(APITestCase): 10 | fixtures = [ 11 | "user.json", "cf_contest.json", "cf_problems.json", "cc_problems.json", 12 | "at_problems.json", "atcoder_contests.json", "cf_solved.json", 13 | "cf_contest.json" 14 | ] 15 | 16 | @classmethod 17 | def setUpTestData(cls): 18 | Profile.objects.filter(owner=1).update(**profile1) 19 | Profile.objects.filter(owner=2).update(**profile2) 20 | 21 | def setUp(self): 22 | self.login_url = reverse('login') 23 | self.user_data = {'username': 'testing', 'password': 'QWERTY@123'} 24 | return super().setUp() 25 | 26 | @classmethod 27 | def login(self, client, login_url, user_data): 28 | user = User.objects.get(username=user_data['username']) 29 | user.set_password(user_data['password']) 30 | user.save() 31 | response = client.post(login_url, user_data, format="json") 32 | return response.data['tokens']['access'] 33 | 34 | @classmethod 35 | def get_authenticated_client(self, token): 36 | client = APIClient() 37 | client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) 38 | return client 39 | 40 | def tearDown(self): 41 | return super().tearDown() 42 | -------------------------------------------------------------------------------- /codedigger/problem/tests/test_upsolve.py: -------------------------------------------------------------------------------- 1 | from django import test 2 | from .test_setup import TestSetUp 3 | from django.urls import reverse 4 | 5 | 6 | class TestUpsolve(TestSetUp): 7 | 8 | def test_codeforces_upsolve(self): 9 | # Deprecated 10 | test_url = reverse('cf-upsolve') 11 | token = self.login(self.client, self.login_url, self.user_data) 12 | client = self.get_authenticated_client(token) 13 | res = client.get(test_url, format="json") 14 | self.assertEqual(res.data['meta']['total'], 1) 15 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 16 | 17 | def test_codeforces_virtual_upsolve(self): 18 | # Deprecated 19 | test_url = reverse('cf-upsolve') + '?virtual=true' 20 | token = self.login(self.client, self.login_url, self.user_data) 21 | client = self.get_authenticated_client(token) 22 | res = client.get(test_url, format="json") 23 | self.assertEqual(res.data['meta']['total'], 2) 24 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 25 | 26 | def test_codechef_upsolve(self): 27 | test_url = reverse('cc-upsolve') 28 | token = self.login(self.client, self.login_url, self.user_data) 29 | client = self.get_authenticated_client(token) 30 | res = client.get(test_url, format="json") 31 | self.assertEqual(len(res.data['result'][0]['problems']), 5) 32 | self.assertEqual(res.data['meta']['total'], 1) 33 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 34 | 35 | def test_atcoder_upsolve(self): 36 | test_url = reverse('at-upsolve') + '?practice=true' 37 | token = self.login(self.client, self.login_url, self.user_data) 38 | client = self.get_authenticated_client(token) 39 | res = client.get(test_url, format="json") 40 | self.assertEqual(len(res.data['result'][0]['problems']), 5) 41 | self.assertEqual(res.data['meta']['total'], 1) 42 | self.assertEqual(len(res.data['result']), res.data['meta']['to']) 43 | -------------------------------------------------------------------------------- /codedigger/problem/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import * 4 | 5 | urlpatterns = [ 6 | path('', SolveProblemsAPIView.as_view(), name="problems"), 7 | path('solved-by-friend/', 8 | ProblemSolvedByFriend.as_view(), 9 | name="solved-by-friend"), 10 | path('upsolve/codeforces', 11 | UpsolveContestAPIView.as_view(), 12 | name='cf-upsolve'), 13 | path('upsolve/codechef', 14 | CCUpsolveContestAPIView.as_view(), 15 | name='cc-upsolve'), 16 | path('upsolve/atcoder', 17 | ATUpsolveContestAPIView.as_view(), 18 | name='at-upsolve'), 19 | ] 20 | -------------------------------------------------------------------------------- /codedigger/social_auth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/social_auth/__init__.py -------------------------------------------------------------------------------- /codedigger/social_auth/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /codedigger/social_auth/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SocialAuthConfig(AppConfig): 5 | name = 'social_auth' 6 | -------------------------------------------------------------------------------- /codedigger/social_auth/google.py: -------------------------------------------------------------------------------- 1 | from google.auth.transport import requests 2 | from google.oauth2 import id_token 3 | 4 | 5 | class Google: 6 | """Google class to fetch the user info and return it""" 7 | 8 | @staticmethod 9 | def validate(auth_token): 10 | """ 11 | validate method Queries the Google oAUTH2 api to fetch the user info 12 | """ 13 | try: 14 | idinfo = id_token.verify_oauth2_token(auth_token, 15 | requests.Request()) 16 | 17 | if 'accounts.google.com' in idinfo['iss']: 18 | return idinfo 19 | 20 | except: 21 | return "The token is either invalid or has expired" 22 | -------------------------------------------------------------------------------- /codedigger/social_auth/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/social_auth/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/social_auth/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /codedigger/social_auth/register.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate 2 | from user.models import User, Profile 3 | import os 4 | from dotenv import load_dotenv, find_dotenv 5 | 6 | load_dotenv(find_dotenv()) 7 | import random 8 | from rest_framework.exceptions import AuthenticationFailed 9 | 10 | 11 | def generate_username(name): 12 | 13 | username = "".join(name.split(' ')).lower() 14 | if not User.objects.filter(username=username).exists(): 15 | return username 16 | else: 17 | random_username = username + str(random.randint(0, 1000)) 18 | return generate_username(random_username) 19 | 20 | 21 | def register_social_user(provider, user_id, email, name): 22 | filtered_user_by_email = User.objects.filter(email=email) 23 | if filtered_user_by_email.exists(): 24 | if provider == filtered_user_by_email[0].auth_provider: 25 | registered_user = authenticate( 26 | username=filtered_user_by_email[0].username, 27 | password=os.getenv('SOCIAL_SECRET')) 28 | first_time = False 29 | temp = Profile.objects.get(owner=registered_user).name 30 | if temp is None: 31 | first_time = True 32 | return { 33 | 'username': registered_user.username, 34 | 'email': registered_user.email, 35 | 'tokens': registered_user.tokens(), 36 | 'first_time': first_time 37 | } 38 | 39 | else: 40 | raise AuthenticationFailed( 41 | detail='Please continue your login using ' + 42 | filtered_user_by_email[0].auth_provider) 43 | 44 | else: 45 | ele = generate_username(name) 46 | user = { 47 | 'username': ele, 48 | 'email': email, 49 | 'password': os.getenv('SOCIAL_SECRET') 50 | } 51 | user = User.objects.create_user(**user) 52 | user.is_verified = True 53 | user.auth_provider = provider 54 | user.save() 55 | new_user = authenticate(username=ele, 56 | password=os.getenv('SOCIAL_SECRET')) 57 | return { 58 | 'email': new_user.email, 59 | 'username': new_user.username, 60 | 'tokens': new_user.tokens(), 61 | 'first_time': True 62 | } 63 | -------------------------------------------------------------------------------- /codedigger/social_auth/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from . import google 3 | from .register import register_social_user 4 | import os 5 | from dotenv import load_dotenv, find_dotenv 6 | 7 | load_dotenv(find_dotenv()) 8 | from rest_framework.exceptions import AuthenticationFailed 9 | 10 | 11 | class GoogleSocialAuthSerializer(serializers.Serializer): 12 | auth_token = serializers.CharField() 13 | 14 | def validate_auth_token(self, auth_token): 15 | user_data = google.Google.validate(auth_token) 16 | try: 17 | user_data['sub'] 18 | except: 19 | raise serializers.ValidationError( 20 | 'The token is invalid or expired. Please login again.') 21 | 22 | if user_data['aud'] != os.getenv('GOOGLE_CLIENT_ID'): 23 | raise AuthenticationFailed('oops, who are you?') 24 | 25 | user_id = user_data['sub'] 26 | email = user_data['email'] 27 | name = user_data['name'] 28 | provider = 'google' 29 | 30 | return register_social_user(provider=provider, 31 | user_id=user_id, 32 | email=email, 33 | name=name) 34 | -------------------------------------------------------------------------------- /codedigger/social_auth/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /codedigger/social_auth/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from .views import GoogleSocialAuthView 4 | 5 | urlpatterns = [ 6 | path('google/', GoogleSocialAuthView.as_view()), 7 | ] -------------------------------------------------------------------------------- /codedigger/social_auth/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status 2 | from rest_framework.response import Response 3 | from rest_framework.generics import GenericAPIView 4 | from .serializers import GoogleSocialAuthSerializer 5 | 6 | 7 | class GoogleSocialAuthView(GenericAPIView): 8 | 9 | serializer_class = GoogleSocialAuthSerializer 10 | 11 | def post(self, request): 12 | """ 13 | POST with "auth_token" 14 | Send an idtoken as from google to get user information 15 | """ 16 | 17 | serializer = self.serializer_class(data=request.data) 18 | serializer.is_valid(raise_exception=True) 19 | data = ((serializer.validated_data)['auth_token']) 20 | return Response(data, status=status.HTTP_200_OK) 21 | -------------------------------------------------------------------------------- /codedigger/templates/codeforces/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 51 | 52 | 53 | 54 | 55 |
 
8 | 9 | 10 | 11 | 12 | 47 | 48 | 49 |
13 | 14 | 15 | 22 | 23 |
16 |
17 | 18 | About Codedigger 19 | 20 |
21 |
24 | 25 | 26 | 27 | 28 | 29 |
 
30 | 31 | 32 | 33 | 34 | 43 | 44 | 45 |
35 | 36 | 37 | 40 | 41 |
38 | UNSUBSCRIBE 39 |
42 |
46 |
50 |
 
56 | -------------------------------------------------------------------------------- /codedigger/templates/user/send_mail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Codedigger - {{message}} 7 | 8 | 9 | 10 | 11 | 12 |
13 | Codedigger - {{message}} 14 |
15 | 16 | 17 |
18 | Codedigger 19 | 20 |

21 | Hello, {{username}}. 22 |

23 | 24 |

25 | Click here to {{message}}. 26 |

27 | 28 |

29 | If for any reason the above link is not working, copy and paste the below link in any browser to {{message}} - 30 |

31 | 32 |

33 | {{link}} 34 |

35 | 36 |

37 | If it was not you, just ignore this mail. 38 |

39 | 40 |

41 | With best regards, 42 |

43 | 44 |

45 | Codedigger Team. 46 |

47 | 48 | For any query Email us: 49 | contact.codedigger@gmail.com 50 |
51 | 52 | -------------------------------------------------------------------------------- /codedigger/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/user/__init__.py -------------------------------------------------------------------------------- /codedigger/user/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import User, Profile, UserFriends 3 | 4 | 5 | class UserAdmin(admin.ModelAdmin): 6 | search_fields = ( 7 | 'username', 8 | 'email', 9 | ) 10 | 11 | 12 | class ProfileAdmin(admin.ModelAdmin): 13 | search_fields = ( 14 | 'owner__username', 15 | 'name', 16 | ) 17 | 18 | 19 | admin.site.register(User, UserAdmin) 20 | admin.site.register(Profile, ProfileAdmin) 21 | admin.site.register(UserFriends) 22 | -------------------------------------------------------------------------------- /codedigger/user/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserConfig(AppConfig): 5 | name = 'user' 6 | -------------------------------------------------------------------------------- /codedigger/user/exception.py: -------------------------------------------------------------------------------- 1 | from rest_framework.exceptions import PermissionDenied 2 | from rest_framework import status 3 | 4 | 5 | class ValidationException(PermissionDenied): 6 | status_code = status.HTTP_400_BAD_REQUEST 7 | default_detail = "Custom Exception Message" 8 | default_code = 'invalid' 9 | 10 | def __init__(self, detail, status_code=None): 11 | self.detail = {'status': "FAILED", 'error': detail} 12 | if status_code is not None: 13 | self.status_code = status_code 14 | 15 | 16 | class AuthenticationException(PermissionDenied): 17 | status_code = status.HTTP_401_UNAUTHORIZED 18 | default_detail = "Custom Exception Message" 19 | default_code = 'invalid' 20 | 21 | def __init__(self, detail, status_code=None): 22 | self.detail = {'status': "FAILED", 'error': detail} 23 | if status_code is not None: 24 | self.status_code = status_code 25 | 26 | 27 | class NotFoundException(PermissionDenied): 28 | status_code = status.HTTP_404_NOT_FOUND 29 | default_detail = "Custom Exception Message" 30 | default_code = 'invalid' 31 | 32 | def __init__(self, detail, status_code=None): 33 | self.detail = {'status': "FAILED", 'error': detail} 34 | if status_code is not None: 35 | self.status_code = status_code 36 | -------------------------------------------------------------------------------- /codedigger/user/handle_validator.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import bs4 3 | 4 | 5 | def check_handle_cf(handle): 6 | if handle is None: 7 | return 0 8 | req = requests.get(' https://codeforces.com/api/user.info?handles=' + 9 | handle) 10 | if req.status_code >= 500: 11 | return 1 12 | if req.status_code == 200: 13 | return 2 14 | return 0 15 | 16 | 17 | def check_handle_spoj(user): 18 | if user is None: 19 | return True 20 | url = 'https://www.spoj.com/users/' + user 21 | res = requests.get(url) 22 | soup = bs4.BeautifulSoup(res.content, 'html.parser') 23 | profile = soup.find('div', {'id': 'user-profile-left'}) 24 | return False if profile == None else True 25 | 26 | 27 | def check_handle_codechef(user): 28 | if user is None: 29 | return True 30 | url = 'https://codechef.com/users/' + user 31 | res = requests.get(url) 32 | soup = bs4.BeautifulSoup(res.content, 'html.parser') 33 | profile = soup.find('section', {'class': 'user-details'}) 34 | if profile is None: 35 | return False 36 | return True 37 | 38 | 39 | def check_handle_atcoder(user): 40 | if user is None: 41 | return True 42 | url = 'https://atcoder.jp/users/' + user 43 | res = requests.get(url) 44 | soup = bs4.BeautifulSoup(res.content, 'html.parser') 45 | profile = soup.find('div', {'class': 'col-md-3 col-sm-12'}) 46 | if profile is None: 47 | return False 48 | return True 49 | 50 | 51 | def check_handle_uva(handle): 52 | if handle is None: 53 | return True 54 | req = requests.get(' https://uhunt.onlinejudge.org/api/uname2uid/' + 55 | handle) 56 | return req.text != '0' 57 | 58 | 59 | def get_uva(handle): 60 | if handle is None: 61 | return None 62 | req = requests.get(' https://uhunt.onlinejudge.org/api/uname2uid/' + 63 | handle) 64 | return req.text 65 | -------------------------------------------------------------------------------- /codedigger/user/migrations/0002_auto_20201228_1110.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2020-12-28 05:40 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='UserFriends', 17 | fields=[ 18 | ('id', 19 | models.AutoField(auto_created=True, 20 | primary_key=True, 21 | serialize=False, 22 | verbose_name='ID')), 23 | ('status', 24 | models.BooleanField(choices=[(True, 25 | 'Friends'), (False, 26 | 'Requested')])), 27 | ('from_user', 28 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 29 | related_name='from_user', 30 | to=settings.AUTH_USER_MODEL)), 31 | ('to_user', 32 | models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 33 | related_name='to_user', 34 | to=settings.AUTH_USER_MODEL)), 35 | ], 36 | ), 37 | migrations.AddField( 38 | model_name='user', 39 | name='friends', 40 | field=models.ManyToManyField(related_name='friends_of', 41 | through='user.UserFriends', 42 | to=settings.AUTH_USER_MODEL), 43 | ), 44 | ] 45 | -------------------------------------------------------------------------------- /codedigger/user/migrations/0003_auto_20201229_2120.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2020-12-29 15:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('user', '0002_auto_20201228_1110'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='profile', 15 | name='atcoder', 16 | field=models.CharField(blank=True, max_length=50, null=True), 17 | ), 18 | migrations.AlterField( 19 | model_name='profile', 20 | name='codechef', 21 | field=models.CharField(blank=True, max_length=50, null=True), 22 | ), 23 | migrations.AlterField( 24 | model_name='profile', 25 | name='spoj', 26 | field=models.CharField(blank=True, max_length=50, null=True), 27 | ), 28 | migrations.AlterField( 29 | model_name='profile', 30 | name='uva_handle', 31 | field=models.CharField(blank=True, max_length=50, null=True), 32 | ), 33 | migrations.AlterField( 34 | model_name='profile', 35 | name='uva_id', 36 | field=models.CharField(blank=True, max_length=50, null=True), 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /codedigger/user/migrations/0003_profile_gurus.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-03 17:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('user', '0002_auto_20201228_1110'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='profile', 15 | name='gurus', 16 | field=models.CharField(blank=True, default='', max_length=300), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/user/migrations/0004_merge_20210104_1504.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-04 09:34 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('user', '0003_auto_20201229_2120'), 10 | ('user', '0003_profile_gurus'), 11 | ] 12 | 13 | operations = [] 14 | -------------------------------------------------------------------------------- /codedigger/user/migrations/0005_auto_20210112_1207.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-12 06:37 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('user', '0004_merge_20210104_1504'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='profile', 15 | name='gurus', 16 | field=models.CharField(blank=True, default=' ', max_length=300), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/user/migrations/0006_auto_20210112_1220.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.4 on 2021-01-12 06:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('user', '0005_auto_20210112_1207'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='profile', 15 | name='gurus', 16 | field=models.CharField(blank=True, default=',', max_length=300), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /codedigger/user/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/user/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/user/param_validators.py: -------------------------------------------------------------------------------- 1 | from .exception import ValidationException 2 | from . import validator_functions as validators 3 | 4 | 5 | def isValidRequest(request, validation_dict): 6 | """ 7 | :param request: Request Object 8 | :param validation_dict: a dictionary of validators 9 | Dict[str(key), str(validation method)] 10 | Example : 11 | { 12 | 'page' : 'required|numeric', 13 | 'per_page' : 'optional' 14 | } 15 | Format of validation method string - 16 | optional/required (default: required) | functions 17 | you can provide default value if optional 18 | """ 19 | 20 | param = request.query_params 21 | for key, val_method in validation_dict: 22 | 23 | methods = val_method.split('|') 24 | 25 | if key in param: 26 | for method in methods: 27 | param = getattr(validators, method)(param=param, key=key) 28 | 29 | elif 'required' in methods: 30 | raise ValidationException('{} is required', key) 31 | -------------------------------------------------------------------------------- /codedigger/user/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework import permissions 2 | from rest_framework.exceptions import APIException 3 | from rest_framework import status 4 | from rest_framework.permissions import BasePermission, SAFE_METHODS 5 | from .models import Profile 6 | 7 | 8 | class AuthenticatedOrReadOnly(BasePermission): 9 | 10 | def has_permission(self, request, view): 11 | if request.user or request.user.is_authenticated or request.method in SAFE_METHODS: 12 | return True 13 | else: 14 | raise Forbidden 15 | 16 | 17 | class ForbiddenAdmin(APIException): 18 | status_code = status.HTTP_403_FORBIDDEN 19 | default_detail = { 20 | 'status': "FAILED", 21 | 'error': 'Only Admins can access this page' 22 | } 23 | 24 | 25 | class AuthenticatedAdmin(permissions.BasePermission): 26 | 27 | def has_permission(self, request, view): 28 | if request.user and request.user.is_authenticated and request.user.is_staff: 29 | return True 30 | else: 31 | raise ForbiddenAdmin 32 | 33 | 34 | class IsOwner(permissions.BasePermission): 35 | 36 | def has_object_permission(self, request, view, obj): 37 | return obj.owner == request.user 38 | 39 | 40 | class Forbidden(APIException): 41 | status_code = status.HTTP_401_UNAUTHORIZED 42 | default_detail = { 43 | 'status': "FAILED", 44 | 'error': 'Authentication credentials were not provided' 45 | } 46 | 47 | 48 | class Authenticated(permissions.BasePermission): 49 | 50 | def has_permission(self, request, view): 51 | if not request.user or not request.user.is_authenticated: 52 | raise Forbidden 53 | else: 54 | return True 55 | 56 | 57 | class ForbiddenActivation(APIException): 58 | status_code = status.HTTP_400_BAD_REQUEST 59 | default_detail = {'status': "FAILED", 'error': 'Account was not activated'} 60 | 61 | 62 | class AuthenticatedActivated(permissions.BasePermission): 63 | 64 | def has_permission(self, request, view): 65 | if request.user and request.user.is_authenticated: 66 | if Profile.objects.get(owner=request.user).codeforces is not None: 67 | return True 68 | else: 69 | raise ForbiddenActivation 70 | else: 71 | raise Forbidden 72 | -------------------------------------------------------------------------------- /codedigger/user/response.py: -------------------------------------------------------------------------------- 1 | from rest_framework.response import Response 2 | from rest_framework import status 3 | 4 | 5 | def response(Data, Status=status.HTTP_200_OK): 6 | 7 | return Response(data={'status': 'OK', 'result': Data}, status=Status) 8 | -------------------------------------------------------------------------------- /codedigger/user/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/user/tests/__init__.py -------------------------------------------------------------------------------- /codedigger/user/tests/test_model.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/user/tests/test_model.py -------------------------------------------------------------------------------- /codedigger/user/tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from rest_framework.test import APITestCase 2 | from django.urls import reverse 3 | #from faker import Faker 4 | 5 | from user.models import User 6 | 7 | 8 | class TestSetUp(APITestCase): 9 | 10 | def setUp(self): 11 | self.register_url = reverse('register') 12 | self.login_url = reverse('login') 13 | #self.fake = Faker() 14 | 15 | self.user_data = { 16 | 'email': 'shivam@mail.com', #self.fake.email(), 17 | 'username': 'shivam', #self.fake.email().split('@')[0], 18 | 'password': 'QWERTY@123' #self.fake.email(), 19 | } 20 | 21 | return super().setUp() 22 | 23 | def tearDown(self): 24 | return super().tearDown() 25 | -------------------------------------------------------------------------------- /codedigger/user/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from .test_setup import TestSetUp 2 | from user.models import User 3 | 4 | 5 | class TestViews(TestSetUp): 6 | 7 | def test_user_cannot_register_with_no_data(self): 8 | res = self.client.post(self.register_url) 9 | self.assertEqual(res.status_code, 400) 10 | 11 | def test_user_can_register_correctly(self): 12 | res = self.client.post(self.register_url, 13 | self.user_data, 14 | format="json") 15 | self.assertEqual(res.data['result']['email'], self.user_data['email']) 16 | self.assertEqual(res.data['result']['username'], 17 | self.user_data['username']) 18 | self.assertEqual(res.status_code, 201) 19 | 20 | def test_user_cannot_login_with_unverified_email(self): 21 | self.client.post(self.register_url, self.user_data, format="json") 22 | res = self.client.post(self.login_url, self.user_data, format="json") 23 | self.assertEqual(res.status_code, 401) 24 | 25 | def test_user_can_login_after_verification(self): 26 | response = self.client.post(self.register_url, 27 | self.user_data, 28 | format="json") 29 | email = response.data['result']['email'] 30 | user = User.objects.get(email=email) 31 | user.is_verified = True 32 | user.save() 33 | res = self.client.post(self.login_url, self.user_data, format="json") 34 | self.assertEqual(res.status_code, 200) 35 | -------------------------------------------------------------------------------- /codedigger/user/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import (RegisterView, VerifyEmail, LoginApiView, ProfileGetView, 3 | ProfileUpdateView, PasswordTokenCheckAPI, 4 | RequestPasswordResetEmail, SetNewPasswordAPIView, 5 | UserProfileGetView, ChangePassword, SendVerificationMail, 6 | CheckAuthView, SearchUser) 7 | # Friend Related View 8 | from .views import SendFriendRequest, RemoveFriend, AcceptFriendRequest, FriendsShowView 9 | from .views import FriendRequestShowView, RequestSendShowView, testing 10 | 11 | from rest_framework_simplejwt.views import TokenRefreshView 12 | 13 | urlpatterns = [ 14 | path('register/', RegisterView.as_view(), name="register"), 15 | path('email-verify/', VerifyEmail.as_view(), name="email-verify"), 16 | path('send-email/', SendVerificationMail.as_view(), name='send-email'), 17 | path('profile/', ProfileGetView.as_view(), name="profile"), 18 | path('profile/', 19 | ProfileUpdateView.as_view(), 20 | name="profile"), 21 | path('profile//', 22 | UserProfileGetView.as_view(), 23 | name="userProfile"), 24 | path('login/', LoginApiView.as_view(), name="login"), 25 | path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), 26 | path('request-reset-email/', 27 | RequestPasswordResetEmail.as_view(), 28 | name="request-reset-email"), 29 | path('password-reset///', 30 | PasswordTokenCheckAPI.as_view(), 31 | name='password-reset-confirm'), 32 | path('password-reset-complete', 33 | SetNewPasswordAPIView.as_view(), 34 | name='password-reset-complete'), 35 | path('password-change/', ChangePassword.as_view(), name='password-change'), 36 | path('check-auth/', CheckAuthView.as_view(), name='check-auth'), 37 | path('password-reset-complete', 38 | SetNewPasswordAPIView.as_view(), 39 | name='password-reset-complete'), 40 | path('search-user', SearchUser.as_view(), name='search-user'), 41 | 42 | # Friends Related Path Start 43 | path('user/send-request', 44 | SendFriendRequest.as_view(), 45 | name='Send_Friend_Request'), 46 | path('user/remove-friend', RemoveFriend.as_view(), name='Remove_Friend'), 47 | path('user/accept-request', 48 | AcceptFriendRequest.as_view(), 49 | name='Accept_Friend_Request'), 50 | path('user/friends', FriendsShowView.as_view(), name='Show_Friends_List'), 51 | path('user/show-request', 52 | FriendRequestShowView.as_view(), 53 | name='Show_Friend_Request_List'), 54 | path('user/show-send-request', 55 | RequestSendShowView.as_view(), 56 | name='Show_Friend_Request_Send_List'), 57 | 58 | # Friends Related Path End 59 | path('testing', testing), 60 | ] 61 | -------------------------------------------------------------------------------- /codedigger/user/utils.py: -------------------------------------------------------------------------------- 1 | from codedigger.settings import EMAIL_HOST_USER 2 | from django.core.mail import send_mail 3 | from django.template.loader import render_to_string 4 | from django.utils.html import strip_tags 5 | 6 | 7 | class Util: 8 | 9 | @staticmethod 10 | def send_email(data): 11 | context = { 12 | 'username': data['email_body']['username'], 13 | 'message': data['email_body']['message'], 14 | 'link': data['email_body']['link'] 15 | } 16 | html_message = render_to_string('user/send_mail.html', context) 17 | plain_message = strip_tags(html_message) 18 | send_mail(data['email_subject'], 19 | plain_message, 20 | EMAIL_HOST_USER, [data['to_email']], 21 | html_message=html_message, 22 | fail_silently=True) 23 | 24 | #send_mail(data['email_subject'],data['email_body'],EMAIL_HOST_USER,[data['to_email']]) 25 | -------------------------------------------------------------------------------- /codedigger/user/validator_functions.py: -------------------------------------------------------------------------------- 1 | from .exception import ValidationException 2 | 3 | 4 | def numeric(param, key, *args, **kwargs): 5 | if not param[key].isnumeric(): 6 | raise ValidationException('{} must be numeric'.format(key)) 7 | return param 8 | 9 | 10 | def alphanumeric(param, key, *args, **kwargs): 11 | if not param[key].isalnum(): 12 | raise ValidationException( 13 | '{} can only contain alphabet or digit'.format(key)) 14 | return param 15 | 16 | 17 | def required(param, *args, **kwargs): 18 | return param 19 | 20 | 21 | def optional(param, *args, **kwargs): 22 | return param 23 | -------------------------------------------------------------------------------- /codedigger/utils/common.py: -------------------------------------------------------------------------------- 1 | def rating_to_difficulty(rating): 2 | if rating <= 1100: 3 | return 'B' 4 | elif rating <= 1500: 5 | return 'E' 6 | elif rating <= 1800: 7 | return 'M' 8 | elif rating <= 2100: 9 | return 'H' 10 | elif rating <= 2600: 11 | return 'S' 12 | else: 13 | return 'C' 14 | -------------------------------------------------------------------------------- /codedigger/utils/email.py: -------------------------------------------------------------------------------- 1 | from django.core.mail import send_mail 2 | from codedigger.settings import EMAIL_HOST_USER 3 | 4 | 5 | def send_testing_mail(sub): 6 | subject = sub 7 | message = 'This is a testing mail send to our testing account.' 8 | recepient = 'testing.codedigger@gmail.com' 9 | send_mail(subject, 10 | message, 11 | EMAIL_HOST_USER, [recepient], 12 | fail_silently=False) 13 | 14 | 15 | def send_error_mail(sub, error): 16 | subject = sub 17 | message = 'OOPS: An error found. This is an error mail send to know error.' 18 | recepient = 'testing.codedigger@gmail.com' 19 | send_mail(subject, 20 | message, 21 | EMAIL_HOST_USER, [recepient], 22 | fail_silently=False) 23 | -------------------------------------------------------------------------------- /codedigger/uva/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/uva/__init__.py -------------------------------------------------------------------------------- /codedigger/uva/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /codedigger/uva/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UvaConfig(AppConfig): 5 | name = 'uva' 6 | -------------------------------------------------------------------------------- /codedigger/uva/cron.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from math import log2 3 | 4 | from problem.models import Problem 5 | 6 | from .utils import rating_to_difficulty 7 | from .api import problem_list 8 | 9 | 10 | def update_uva_problems(): 11 | 12 | try: 13 | data = problem_list() 14 | except: 15 | return 16 | 17 | for p in data: 18 | 19 | if len(Problem.objects.filter(prob_id=p[0])) > 0: 20 | continue 21 | 22 | new_problem = Problem() 23 | new_problem.prob_id = p[0] 24 | new_problem.index = p[1] 25 | new_problem.name = p[2] 26 | dacu = p[3] 27 | wa = p[16] 28 | 29 | if dacu != 0: 30 | rating = min(3600, max(800, (20 - min(log2(dacu), 20)) * 200)) 31 | new_problem.rating = str(int(rating)) 32 | new_problem.difficulty = rating_to_difficulty(rating) 33 | 34 | new_problem.platform = 'U' 35 | new_problem.url = "https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=" + str( 36 | p[0]) 37 | new_problem.save() 38 | 39 | url = "https://uhunt.onlinejudge.org/api/cpbook/3" 40 | res = requests.get(url) 41 | data = res.json() 42 | 43 | for p in data: 44 | title1 = p['title'] 45 | for q in p['arr']: 46 | title2 = q['title'] 47 | for r in q['arr']: 48 | title3 = r[0] 49 | for i in range(1, len(r)): 50 | prob_num = abs(r[i]) 51 | tags = [title1, title2, title3] 52 | Problem.objects.filter(index=prob_num).update(tags=tags) 53 | -------------------------------------------------------------------------------- /codedigger/uva/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/uva/migrations/__init__.py -------------------------------------------------------------------------------- /codedigger/uva/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /codedigger/uva/serializers.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/uva/serializers.py -------------------------------------------------------------------------------- /codedigger/uva/urls.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/codedigger/uva/urls.py -------------------------------------------------------------------------------- /codedigger/uva/utils.py: -------------------------------------------------------------------------------- 1 | def rating_to_difficulty(rating): 2 | if rating <= 1100: 3 | return 'B' 4 | elif rating <= 1500: 5 | return 'E' 6 | elif rating <= 1800: 7 | return 'M' 8 | elif rating <= 2100: 9 | return 'H' 10 | elif rating <= 2600: 11 | return 'S' 12 | else: 13 | return 'C' -------------------------------------------------------------------------------- /codedigger/uva/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | docs.codedigger.tech -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-dinky -------------------------------------------------------------------------------- /docs/format.md: -------------------------------------------------------------------------------- 1 | **Title** 2 | ---- 3 | <_Additional information about your API call. Try to use verbs that match both request type (fetching vs modifying) and plurality (one vs multiple)._> 4 | 5 | * **URL** 6 | 7 | <_The URL Structure (path only, no root url)_> 8 | 9 | * **Method:** 10 | 11 | <_The request type_> 12 | 13 | `GET` | `POST` | `DELETE` | `PUT` 14 | 15 | * **URL Params** 16 | 17 | <_If URL params exist, specify them in accordance with name mentioned in URL section. Separate into optional and required. Document data constraints._> 18 | 19 | **Required:** 20 | 21 | `id=[integer]` 22 | 23 | **Optional:** 24 | 25 | `photo_id=[alphanumeric]` 26 | 27 | * **Data Params** 28 | 29 | <_If making a post request, what should the body payload look like? URL Params rules apply here too._> 30 | 31 | * **Success Response:** 32 | 33 | <_What should the status code be on success and is there any returned data? This is useful when people need to to know what their callbacks should expect!_> 34 | 35 | * **Code:** 200
36 | **Content:** `{ id : 12 }` 37 | 38 | * **Error Response:** 39 | 40 | <_Most endpoints will have many ways they can fail. From unauthorized access, to wrongful parameters etc. All of those should be liste d here. It might seem repetitive, but it helps prevent assumptions from being made where they should be._> 41 | 42 | * **Code:** 401 UNAUTHORIZED
43 | **Content:** `{ error : "Log in" }` 44 | 45 | OR 46 | 47 | * **Code:** 422 UNPROCESSABLE ENTRY
48 | **Content:** `{ error : "Email Invalid" }` 49 | 50 | * **Sample Call:** 51 | 52 | <_Just a sample call to your endpoint in a runnable format ($.ajax call or a curl request) - this makes life easier and more predictable._> 53 | 54 | * **Notes:** 55 | 56 | <_This is where all uncertainties, commentary, discussion etc. can go. I recommend timestamping and identifying oneself when leaving comments here._> -------------------------------------------------------------------------------- /docs/routes/static/images/codedigger-logo-48px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Code-dig-ger/Backend/9ab1b57436a0a1a6197777c0b36c842e71121d3a/docs/routes/static/images/codedigger-logo-48px.png -------------------------------------------------------------------------------- /docs/routes/static/js/docs.js: -------------------------------------------------------------------------------- 1 | // utilities 2 | var get = function (selector, scope) { 3 | scope = scope ? scope : document; 4 | return scope.querySelector(selector); 5 | }; 6 | 7 | var getAll = function (selector, scope) { 8 | scope = scope ? scope : document; 9 | return scope.querySelectorAll(selector); 10 | }; 11 | 12 | // setup typewriter effect in the terminal demo 13 | if (document.getElementsByClassName('demo').length > 0) { 14 | var i = 0; 15 | var txt = `curl -X GET "https://api.codedigger.tech/welcome" 16 | 17 | Welcome to Codedigger REST API 👋`; 18 | var speed = 60; 19 | 20 | function typeItOut () { 21 | if (i < txt.length) { 22 | document.getElementsByClassName('demo')[0].innerHTML += txt.charAt(i); 23 | i++; 24 | setTimeout(typeItOut, speed); 25 | } 26 | } 27 | 28 | setTimeout(typeItOut, 1800); 29 | } 30 | 31 | // toggle tabs on codeblock 32 | window.addEventListener("load", function() { 33 | // get all tab_containers in the document 34 | var tabContainers = getAll(".tab__container"); 35 | 36 | // bind click event to each tab container 37 | for (var i = 0; i < tabContainers.length; i++) { 38 | get('.tab__menu', tabContainers[i]).addEventListener("click", tabClick); 39 | } 40 | 41 | // each click event is scoped to the tab_container 42 | function tabClick (event) { 43 | var scope = event.currentTarget.parentNode; 44 | var clickedTab = event.target; 45 | var tabs = getAll('.tab', scope); 46 | var panes = getAll('.tab__pane', scope); 47 | var activePane = get(`.${clickedTab.getAttribute('data-tab')}`, scope); 48 | 49 | // remove all active tab classes 50 | for (var i = 0; i < tabs.length; i++) { 51 | tabs[i].classList.remove('active'); 52 | } 53 | 54 | // remove all active pane classes 55 | for (var i = 0; i < panes.length; i++) { 56 | panes[i].classList.remove('active'); 57 | } 58 | 59 | // apply active classes on desired tab and pane 60 | clickedTab.classList.add('active'); 61 | activePane.classList.add('active'); 62 | } 63 | }); 64 | 65 | //in page scrolling for documentaiton page 66 | var btns = getAll('.js-btn'); 67 | var sections = getAll('.js-section'); 68 | 69 | function setActiveLink(event) { 70 | // remove all active tab classes 71 | for (var i = 0; i < btns.length; i++) { 72 | btns[i].classList.remove('selected'); 73 | btns[i].firstElementChild.classList.remove('selected'); 74 | } 75 | 76 | event.target.classList.add('selected'); 77 | } 78 | 79 | function smoothScrollTo(i, event) { 80 | var element = sections[i]; 81 | setActiveLink(event); 82 | 83 | window.scrollTo({ 84 | 'behavior': 'smooth', 85 | 'top': element.offsetTop - 20, 86 | 'left': 0 87 | }); 88 | } 89 | 90 | if (btns.length && sections.length > 0) { 91 | for (var i = 0; i