├── web
├── web
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── urls.cpython-37.pyc
│ │ ├── wsgi.cpython-37.pyc
│ │ ├── __init__.cpython-37.pyc
│ │ └── settings.cpython-37.pyc
│ ├── wsgi.py
│ ├── urls.py
│ └── settings.py
├── questionFilter
│ ├── __init__.py
│ ├── migrations
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ └── 0001_initial.cpython-37.pyc
│ │ └── 0001_initial.py
│ ├── templatetags
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ └── custom_tags.cpython-37.pyc
│ │ └── custom_tags.py
│ ├── tests.py
│ ├── apps.py
│ ├── __pycache__
│ │ ├── admin.cpython-37.pyc
│ │ ├── forms.cpython-37.pyc
│ │ ├── models.cpython-37.pyc
│ │ ├── views.cpython-37.pyc
│ │ └── __init__.cpython-37.pyc
│ ├── forms.py
│ ├── admin.py
│ ├── models.py
│ └── views.py
├── .ignore
├── db.sqlite3
├── manage.py
├── templets
│ └── questionFilter
│ │ ├── question_detail.html
│ │ └── homepage.html
└── crawler.py
└── README.md
/web/web/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/questionFilter/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/questionFilter/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/questionFilter/templatetags/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/.ignore:
--------------------------------------------------------------------------------
1 | *.log
2 | local_settings.py
3 | db.sqlite3
4 | db.sqlite3-journal
--------------------------------------------------------------------------------
/web/db.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/db.sqlite3
--------------------------------------------------------------------------------
/web/questionFilter/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/web/web/__pycache__/urls.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/web/__pycache__/urls.cpython-37.pyc
--------------------------------------------------------------------------------
/web/web/__pycache__/wsgi.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/web/__pycache__/wsgi.cpython-37.pyc
--------------------------------------------------------------------------------
/web/web/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/web/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/web/web/__pycache__/settings.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/web/__pycache__/settings.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class QuestionfilterConfig(AppConfig):
5 | name = 'questionFilter'
6 |
--------------------------------------------------------------------------------
/web/questionFilter/__pycache__/admin.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/__pycache__/admin.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/__pycache__/forms.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/__pycache__/forms.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/__pycache__/models.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/__pycache__/models.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/__pycache__/views.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/__pycache__/views.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/migrations/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/migrations/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/templatetags/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/templatetags/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/migrations/__pycache__/0001_initial.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/migrations/__pycache__/0001_initial.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/templatetags/__pycache__/custom_tags.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lkw222/LeetcodeHelper/HEAD/web/questionFilter/templatetags/__pycache__/custom_tags.cpython-37.pyc
--------------------------------------------------------------------------------
/web/questionFilter/templatetags/custom_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 | @register.filter
6 | def get_key(value, arg):
7 | return value.get(arg, None)
8 |
--------------------------------------------------------------------------------
/web/questionFilter/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | class Search(forms.Form):
4 | google = forms.BooleanField(label="Google test")
5 | amazon = forms.BooleanField()
6 | facebook = forms.BooleanField()
7 |
--------------------------------------------------------------------------------
/web/questionFilter/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from questionFilter.models import *
3 | # Register your models here.
4 | admin.site.register(Question)
5 | admin.site.register(Company)
6 | admin.site.register(CompanyTag)
7 | admin.site.register(Algorithm)
8 | admin.site.register(AlgorithmTag)
9 | admin.site.register(Similar)
10 |
--------------------------------------------------------------------------------
/web/web/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for web 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/2.2/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', 'web.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/web/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 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web.settings')
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError as exc:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | ) from exc
17 | execute_from_command_line(sys.argv)
18 |
19 |
20 | if __name__ == '__main__':
21 | main()
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LeetcodeHelper
2 |
3 | This project built a website which provides an advanced multiple filter for users to get more extensive use of leetcode.
4 |
5 | • Utilized Requests library and GraphQL API to crawl the question details from leetcode website.
6 |
7 | • Used multiprocessing to improve performance 200%.
8 |
9 | • Designed the relational database by using Django object-relational mapper (ORM) to store the data fetched.
10 |
11 | • Successfully created the website back-end by using Django MTV framework.
12 |
13 | • Built an interactive UI based on jQuery and CSS, embellished the interface of the website with Bootstrap.
14 |
15 | • Delivered the functional multiple filter, search and ranking by using Django model query.
16 |
17 | • Deployed the website on PythonAnywhere and got more than 5k visits within first week after launch.
18 |
--------------------------------------------------------------------------------
/web/templets/questionFilter/question_detail.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
15 |
{{detail.front_id}}.{{detail.name}}
16 |
17 |
18 |
19 |
20 |
21 | {% autoescape off %}
22 | {{detail.content}}
23 | {% endautoescape %}
24 |
25 |
26 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/web/web/urls.py:
--------------------------------------------------------------------------------
1 | """web URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import path, re_path
18 | from questionFilter import views
19 |
20 | urlpatterns = [
21 | path('admin/', admin.site.urls),
22 | path('', views.home),
23 | re_path(r'question_detail/(.+)/$', views.question_detail),
24 | re_path(r'company_filter/(.+)/$', views.company_filter),
25 | re_path(r'algorithm_filter/(.+)/$', views.algorithm_filter),
26 | re_path(r'difficulty_filter/(.+)/$', views.difficulty_filter),
27 | re_path(r'question_sort/(.+)/$', views.question_sort),
28 | re_path(r'like_filter/(.+)/$', views.like_filter)
29 |
30 | ]
31 |
--------------------------------------------------------------------------------
/web/questionFilter/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
5 | class Question(models.Model):
6 | question_slug = models.SlugField(primary_key=True, unique=True)
7 | front_id = models.IntegerField()
8 | content = models.TextField()
9 | name = models.CharField(max_length=100)
10 | difficulty = models.CharField(max_length=10)
11 | total_accept = models.IntegerField()
12 | total_submission = models.IntegerField()
13 | accept_rate = models.FloatField()
14 | paid_only = models.BooleanField()
15 | like = models.IntegerField()
16 | dislike = models.IntegerField()
17 | frequency = models.FloatField()
18 |
19 | def __str__(self):
20 | return self.name
21 |
22 |
23 | class Company(models.Model):
24 | company_slug = models.SlugField(primary_key=True, unique=True)
25 | name = models.CharField(max_length=100)
26 | hire_link = models.URLField(null=True)
27 |
28 | def __str__(self):
29 | return self.name
30 |
31 |
32 | class CompanyTag(models.Model):
33 | company_slug = models.ForeignKey(Company, on_delete=True)
34 | question_slug = models.ForeignKey(Question, on_delete=True)
35 | vote_count = models.IntegerField()
36 |
37 | def __str__(self):
38 | return self.company_slug.name + ': ' + self.question_slug.name
39 |
40 | class Algorithm(models.Model):
41 | algorithm_slug = models.SlugField(primary_key=True, unique=True)
42 | name = models.CharField(max_length=100)
43 | topic_link = models.URLField(null=True)
44 |
45 | def __str__(self):
46 | return self.name
47 |
48 |
49 | class AlgorithmTag(models.Model):
50 | algorithm_slug = models.ForeignKey(Algorithm, on_delete=True)
51 | question_slug = models.ForeignKey(Question, on_delete=True)
52 |
53 | def __str__(self):
54 | return self.algorithm_slug.name + ': ' + self.question_slug.name
55 |
56 | class Similar(models.Model):
57 | cur_question_slug = models.ForeignKey(Question, on_delete=True, related_name='%(class)s_requests_created')
58 | tar_question_slug = models.ForeignKey(Question, on_delete=True)
59 | tar_difficulty = models.CharField(max_length=10)
60 |
61 | def __str__(self):
62 | return self.cur_question_slug.name + ' -> ' + self.tar_question_slug.name
63 |
--------------------------------------------------------------------------------
/web/questionFilter/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.1 on 2019-08-26 20:18
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | initial = True
9 |
10 | dependencies = [
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Algorithm',
16 | fields=[
17 | ('algorithm_slug', models.SlugField(primary_key=True, serialize=False, unique=True)),
18 | ('name', models.CharField(max_length=100)),
19 | ('topic_link', models.URLField(null=True)),
20 | ],
21 | ),
22 | migrations.CreateModel(
23 | name='Company',
24 | fields=[
25 | ('company_slug', models.SlugField(primary_key=True, serialize=False, unique=True)),
26 | ('name', models.CharField(max_length=100)),
27 | ('hire_link', models.URLField(null=True)),
28 | ],
29 | ),
30 | migrations.CreateModel(
31 | name='Question',
32 | fields=[
33 | ('question_slug', models.SlugField(primary_key=True, serialize=False, unique=True)),
34 | ('front_id', models.IntegerField()),
35 | ('content', models.TextField()),
36 | ('name', models.CharField(max_length=100)),
37 | ('difficulty', models.CharField(max_length=10)),
38 | ('total_accept', models.IntegerField()),
39 | ('total_submission', models.IntegerField()),
40 | ('accept_rate', models.FloatField()),
41 | ('paid_only', models.BooleanField()),
42 | ('like', models.IntegerField()),
43 | ('dislike', models.IntegerField()),
44 | ('frequency', models.FloatField()),
45 | ],
46 | ),
47 | migrations.CreateModel(
48 | name='Similar',
49 | fields=[
50 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
51 | ('tar_difficulty', models.CharField(max_length=10)),
52 | ('cur_question_slug', models.ForeignKey(on_delete=True, related_name='similar_requests_created', to='questionFilter.Question')),
53 | ('tar_question_slug', models.ForeignKey(on_delete=True, to='questionFilter.Question')),
54 | ],
55 | ),
56 | migrations.CreateModel(
57 | name='CompanyTag',
58 | fields=[
59 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
60 | ('vote_count', models.IntegerField()),
61 | ('company_slug', models.ForeignKey(on_delete=True, to='questionFilter.Company')),
62 | ('question_slug', models.ForeignKey(on_delete=True, to='questionFilter.Question')),
63 | ],
64 | ),
65 | migrations.CreateModel(
66 | name='AlgorithmTag',
67 | fields=[
68 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
69 | ('algorithm_slug', models.ForeignKey(on_delete=True, to='questionFilter.Algorithm')),
70 | ('question_slug', models.ForeignKey(on_delete=True, to='questionFilter.Question')),
71 | ],
72 | ),
73 | ]
74 |
--------------------------------------------------------------------------------
/web/web/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for web project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.2.1.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.2/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.2/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 | TEMPLATES_DIR = os.path.join(BASE_DIR, 'templets')
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'eolzocyv6dg-9_n&u)71)efff29yynhwaip=3!(ski^aijz5q7'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'questionFilter',
41 | ]
42 |
43 | MIDDLEWARE = [
44 | 'django.middleware.security.SecurityMiddleware',
45 | 'django.contrib.sessions.middleware.SessionMiddleware',
46 | 'django.middleware.common.CommonMiddleware',
47 | 'django.middleware.csrf.CsrfViewMiddleware',
48 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
49 | 'django.contrib.messages.middleware.MessageMiddleware',
50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
51 | ]
52 |
53 | ROOT_URLCONF = 'web.urls'
54 |
55 | TEMPLATES = [
56 | {
57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
58 | 'DIRS': [TEMPLATES_DIR],
59 | 'APP_DIRS': True,
60 | 'OPTIONS': {
61 | 'context_processors': [
62 | 'django.template.context_processors.debug',
63 | 'django.template.context_processors.request',
64 | 'django.contrib.auth.context_processors.auth',
65 | 'django.contrib.messages.context_processors.messages',
66 | ],
67 | },
68 | },
69 | ]
70 |
71 | WSGI_APPLICATION = 'web.wsgi.application'
72 |
73 |
74 | # Database
75 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
76 |
77 | DATABASES = {
78 | 'default': {
79 | 'ENGINE': 'django.db.backends.sqlite3',
80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
81 | }
82 | }
83 |
84 |
85 | # Password validation
86 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
87 |
88 | AUTH_PASSWORD_VALIDATORS = [
89 | {
90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
91 | },
92 | {
93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
94 | },
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
100 | },
101 | ]
102 |
103 |
104 | # Internationalization
105 | # https://docs.djangoproject.com/en/2.2/topics/i18n/
106 |
107 | LANGUAGE_CODE = 'en-us'
108 |
109 | TIME_ZONE = 'UTC'
110 |
111 | USE_I18N = True
112 |
113 | USE_L10N = True
114 |
115 | USE_TZ = True
116 |
117 |
118 | # Static files (CSS, JavaScript, Images)
119 | # https://docs.djangoproject.com/en/2.2/howto/static-files/
120 |
121 | STATIC_URL = '/static/'
122 |
--------------------------------------------------------------------------------
/web/questionFilter/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, redirect
2 | from questionFilter.models import *
3 | from questionFilter.forms import *
4 | from django.core.paginator import Paginator
5 | from django.http import HttpResponseRedirect
6 | from django.urls import reverse
7 | from django.db.models import Q
8 | from django.db.models import F
9 |
10 |
11 | # Create your views here.
12 | def home(request):
13 | data = {}
14 | data['companys'] = Company.objects.all().order_by('company_slug')
15 | data['algorithms'] = Algorithm.objects.all().order_by('algorithm_slug')
16 | company_selected = request.session.get('company_selected', {})
17 | data['company_selected'] = company_selected
18 | algorithm_selected = request.session.get('algorithm_selected', {})
19 | data['algorithm_selected'] = algorithm_selected
20 | difficulty_selected = request.session.get('difficulty_selected', {})
21 | data['difficulty_selected'] = difficulty_selected
22 | like_ratio = request.session.get('like_ratio', 'all')
23 | data['like_ratio'] = like_ratio
24 | questions = Question.objects.all()
25 |
26 | # Company
27 | if len(company_selected) > 0:
28 | for cur_company in company_selected:
29 | questions = questions.filter(companytag__company_slug__company_slug__exact=cur_company)
30 |
31 | # Tag
32 | if len(algorithm_selected) > 0:
33 | for cur_algorithm in algorithm_selected:
34 | questions = questions.filter(algorithmtag__algorithm_slug__algorithm_slug__exact=cur_algorithm)
35 |
36 | # Difficulty
37 | if 0 < len(difficulty_selected) < 3:
38 | questions = questions.filter(difficulty__in=difficulty_selected)
39 |
40 | # Like Ratio
41 | if like_ratio == "Best":
42 | questions = questions.filter(like__gte=F('dislike')*10)
43 | elif like_ratio == "Excellent":
44 | questions = questions.filter(Q(like__lt=F('dislike')*10) & Q(like__gte=F('dislike')*1))
45 | elif like_ratio == "Normal":
46 | questions = questions.filter(Q(like__lt=F('dislike')*1) & Q(like__gte=F('dislike')*0.5))
47 | elif like_ratio == "CRAZE":
48 | questions = questions.filter(Q(like__lt=F('dislike')*0.5) & Q(like__gte=F('dislike')*0.1))
49 | elif like_ratio == "SHIT":
50 | questions = questions.filter(like__lte=F('dislike')*0.1)
51 | else:
52 | pass
53 |
54 |
55 | # Search
56 | if request.method == 'POST':
57 | content = request.POST['search_content']
58 | ids = []
59 | names = []
60 | for cur in content.split():
61 | try:
62 | ids.append(int(cur))
63 | except:
64 | names.append(cur)
65 | if names != [] and ids != []:
66 | questions = questions.filter(Q(name__icontains=' '.join(names)) & Q(front_id__in=ids))
67 | elif ids != []:
68 | questions = questions.filter(Q(front_id__in=ids))
69 | elif names != []:
70 | questions = questions.filter(Q(name__icontains=' '.join(names)))
71 | else:
72 | pass
73 | # Sort
74 | sort_type = request.session.get('sort_type', 'id')
75 | if sort_type == 'id':
76 | questions = questions.order_by('front_id')
77 | elif sort_type == 'frequency':
78 | questions = questions.order_by('-frequency')
79 | elif sort_type == 'AC':
80 | questions = questions.order_by('-accept_rate')
81 | elif sort_type == 'name':
82 | questions = questions.order_by('question_slug')
83 | data['type'] = sort_type
84 |
85 | for question in questions:
86 | question.frequency /= 0.05
87 |
88 | #Pagination
89 | limit = 50
90 | paginator = Paginator(questions, limit)
91 | page = request.GET.get('page')
92 | contacts = paginator.get_page(page)
93 | page_range = get_pane_range(paginator, 5)
94 |
95 | data['questions'] = contacts
96 | data['page_range'] = page_range
97 | return render(request, 'questionFilter/homepage.html', data)
98 |
99 |
100 | def company_filter(request, company_slug):
101 | company_selected = request.session.get('company_selected', {})
102 | if company_slug in company_selected:
103 | del company_selected[company_slug]
104 | else:
105 | company_selected[company_slug] = None
106 |
107 | data = {'company_selected':company_selected}
108 | request.session['company_selected'] = company_selected
109 | return HttpResponseRedirect("/")
110 |
111 |
112 | def algorithm_filter(request, algorithm_slug):
113 | algorithm_selected = request.session.get('algorithm_selected', {})
114 | if algorithm_slug in algorithm_selected:
115 | del algorithm_selected[algorithm_slug]
116 | else:
117 | algorithm_selected[algorithm_slug] = None
118 |
119 | data = {'algorithm_selected':algorithm_selected}
120 | request.session['algorithm_selected'] = algorithm_selected
121 | return HttpResponseRedirect("/")
122 |
123 |
124 | def difficulty_filter(request, difficulty):
125 | difficulty_selected = request.session.get('difficulty_selected', {})
126 | if difficulty in difficulty_selected:
127 | del difficulty_selected[difficulty]
128 | else:
129 | difficulty_selected[difficulty] = None
130 |
131 | data = {'difficulty_selected':difficulty_selected}
132 | request.session['difficulty_selected'] = difficulty_selected
133 | return HttpResponseRedirect("/")
134 |
135 | def question_sort(request, sort_type):
136 | request.session['sort_type'] = sort_type
137 | return HttpResponseRedirect("/")
138 |
139 | def get_pane_range(paginator, display_num):
140 | max_page = paginator.num_pages
141 | page_range = {}
142 | page_range[1] = range(1, min(max_page, display_num)+1)
143 | page_range[2] = range(1, min(max_page, display_num)+1)
144 | page_range[max_page-1] = range(max(1, max_page+1-display_num), max_page)
145 | page_range[max_page] = range(max(1, max_page+1-display_num), max_page)
146 | return page_range
147 |
148 |
149 | def like_filter(request, like_ratio):
150 | request.session['like_ratio'] = like_ratio
151 | return HttpResponseRedirect("/")
152 |
153 |
154 | def question_detail(request, question_slug):
155 | detail = Question.objects.get(question_slug=question_slug)
156 | data = {'detail':detail}
157 | return render(request, 'questionFilter/question_detail.html', data)
158 |
--------------------------------------------------------------------------------
/web/templets/questionFilter/homepage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | LeetcodeHelper
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {% load custom_tags %}
14 |
15 |
16 |
Hi, Welcome to LeetcodeHelper
17 |
This website provides an advanced multiple filter for users to get more extensive use of leetcode. You can select
18 | questions based on whatever condition you want. For example, you can choose questions encountered in
19 | interviews of companies like Google Amazon, and tags of algorithms used (dp,dfs). You can also sort these questions by conditions like frequency...
20 | Beyond that, you can also search for some key words or certain ID.
21 |
22 |
23 |
24 |
25 |
44 |
45 |
46 |
47 |
48 |
49 | Company
50 |
51 |
52 |
53 |
54 | {% for company in companys %}
55 | {% if company_selected and company.company_slug in company_selected %}
56 |
{{company.name}}
57 | {% else %}
58 |
{{company.name}}
59 | {% endif %}
60 | {% endfor %}
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Tag
71 |
72 |
73 |
74 |
75 | {% for algorithm in algorithms %}
76 | {% if algorithm_selected and algorithm.algorithm_slug in algorithm_selected %}
77 |
{{algorithm.name}}
78 | {% else %}
79 |
{{algorithm.name}}
80 | {% endif %}
81 | {% endfor %}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
93 |
101 |
102 |
103 |
104 |
105 |
106 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | #
129 | Name
130 | Difficulty
131 | AC Rate
132 | Frequency
133 |
134 |
135 |
136 | {% for question in questions %}
137 |
138 | {{question.front_id}}
139 |
140 | {{question.name}}
141 | {% if question.paid_only %}
142 |
143 | {% endif %}
144 |
145 |
146 | {% if question.difficulty == 'Easy' %}
147 | Easy
148 | {% elif question.difficulty == 'Medium' %}
149 | Medium
150 | {% else %}
151 | Hard
152 | {% endif %}
153 |
154 | {{question.accept_rate}}%
155 |
156 | {% if question.frequency <= 20 %}
157 |
160 | {% elif 20 < question.frequency and question.frequency < 60 %}
161 |
164 | {% else %}
165 |
168 | {% endif %}
169 |
170 |
171 | {% endfor %}
172 |
173 |
174 |
175 |
176 |
177 |
222 |
223 |
224 |
225 |
226 |
227 |
--------------------------------------------------------------------------------
/web/crawler.py:
--------------------------------------------------------------------------------
1 | # from bs4 import BeautifulSoup
2 | # from urllib.request import urlopen
3 | import os
4 | import django
5 | import requests
6 | import json
7 | # import csv
8 | import multiprocessing
9 | import time
10 | import re
11 |
12 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web.settings')
13 | django.setup()
14 |
15 | from questionFilter.models import *
16 |
17 |
18 | class LeetCodeCrawler(object):
19 | def __init__(self, user, password):
20 | self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
21 | self.session = requests.Session()
22 | self.base_url = 'https://leetcode.com/'
23 | self.is_premium = False
24 | self.headers = {'User-Agent': self.user_agent}
25 | r = self.session.get(self.base_url, headers=self.headers)
26 | csrftoken = re.search(r'(?<=csrftoken=).*?(?=;)', r.headers['Set-Cookie']).group(0)
27 | self.headers['x-csrftoken'] = csrftoken
28 | self.max_retry_time = 10
29 | self.is_login = self.login(user, password)
30 | if self.is_login:
31 | print('login successfully!')
32 | else:
33 | print('login failed!')
34 |
35 | def login(self, user, password):
36 | current_headers = self.headers.copy()
37 | login_url = 'https://leetcode.com/accounts/login/'
38 | login_info = {'csrfmiddlewaretoken':current_headers['x-csrftoken'],
39 | 'login':'lionpowder',
40 | 'password':'199488lkw@@',
41 | # 'login':'xiyu@oregonstate.edu',
42 | # 'password':'960615',
43 | 'next':'/problems'
44 | }
45 | current_headers['referer'] = 'https://leetcode.com/accounts/login/'
46 |
47 | query = {'operationName': "globalData",
48 | 'query': '''query globalData {
49 | isCurrentUserAuthenticated
50 | isPremium
51 | }'''
52 | }
53 | verify = None
54 | retry_time = 0
55 | while verify is None and retry_time < self.max_retry_time:
56 | try:
57 | retry_time += 1
58 | print('retry: ',retry_time)
59 | log = self.session.post(login_url, headers = current_headers, timeout=2, data = login_info, allow_redirects = False)
60 | current_headers['content-type'] = 'application/json'
61 | verify = self.session.post('https://leetcode.com/graphql', headers = current_headers, timeout=2, data=json.dumps(query)).json()
62 | except:
63 | pass
64 | if verify is None:
65 | print("Something Wrong")
66 | return False
67 | else:
68 | self.is_premium = verify['data']['isPremium']
69 | return verify['data']['isCurrentUserAuthenticated']
70 |
71 | def get_questions_list(self):
72 | all_questions_url = 'https://leetcode.com/api/problems/all/'
73 | all_questions = self.session.get(all_questions_url, headers=self.headers)
74 | return all_questions.json()
75 |
76 | def save_to_db(self, detail, paid_only, frequency):
77 | detail = detail['data']
78 | question = detail['question']
79 |
80 | question_slug = question['questionTitleSlug']
81 | front_id = question['questionFrontendId']
82 | content = question['content']
83 | name = question['questionTitle']
84 | difficulty = question['difficulty']
85 | stats = eval(question['stats'])
86 | total_accept = stats['totalAcceptedRaw']
87 | total_submission = stats['totalSubmissionRaw']
88 | accept_rate = stats['acRate'][:-1]
89 | like = question['likes']
90 | dislike = question['dislikes']
91 |
92 | # slimilars = question['similarQuestions']
93 | # company_tags = question['companyTags']
94 | # company_stats = question['companyTagStats']
95 | db_question = Question.objects.get_or_create(question_slug = question_slug,
96 | front_id=front_id,
97 | content=content,
98 | name=name,
99 | difficulty=difficulty,
100 | total_accept=total_accept,
101 | total_submission=total_submission,
102 | accept_rate=accept_rate,
103 | paid_only=paid_only,
104 | like=like,
105 | dislike=dislike,
106 | frequency=frequency)
107 |
108 |
109 | if db_question[1]:
110 | db_question[0].save()
111 | print(db_question)
112 | # db_companies = question['companyTags']
113 | company_tags = json.loads(question['companyTagStats']).values()
114 | # if len(db_companies) > 0:
115 | # for company in db_companies:
116 | # company_slug = company['slug']
117 | # name = company['name']
118 | # db_company = Company.objects.get_or_create(company_slug=company_slug,
119 | # name=name)
120 | # # db_company.save()
121 | #
122 | for block in company_tags:
123 | for company_tag in block:
124 | company_slug = company_tag['slug']
125 | name = company_tag['name']
126 | db_company = Company.objects.get_or_create(company_slug=company_slug,
127 | name=name)
128 | if db_company[1]:
129 | db_company[0].save()
130 | vote_count = company_tag['timesEncountered']
131 | db_company_tag = CompanyTag.objects.get_or_create(company_slug=db_company[0],
132 | question_slug=db_question[0],
133 | vote_count=vote_count)
134 | if db_company_tag[1]:
135 | db_company_tag[0].save()
136 | algorithm_tags = question['topicTags']
137 | for algorithm_tag in algorithm_tags:
138 | algorithm_slug = algorithm_tag['slug']
139 | name = algorithm_tag['name']
140 | db_algorithm = Algorithm.objects.get_or_create(algorithm_slug=algorithm_slug,
141 | name=name)
142 | if db_algorithm[1]:
143 | db_algorithm[0].save()
144 | db_algorithm_tag = AlgorithmTag.objects.get_or_create(algorithm_slug=db_algorithm[0],
145 | question_slug=db_question[0])
146 | if db_algorithm_tag[1]:
147 | db_algorithm_tag[0].save()
148 |
149 | similars = json.loads(question['similarQuestions'])
150 | for similar in similars:
151 | tar_question_slug = similar['titleSlug']
152 | tar_difficulty = similar['difficulty']
153 | try:
154 | db_tar_question = Question.objects.get(question_slug=tar_question_slug)
155 | db_similar1 = Similar.objects.get_or_create(cur_question_slug=db_question[0],
156 | tar_question_slug=db_tar_question,
157 | tar_difficulty=tar_difficulty)
158 | if db_similar1[1]:
159 | db_similar1[0].save()
160 | db_similar2 = Similar.objects.get_or_create(cur_question_slug=db_tar_question,
161 | tar_question_slug=db_question[0],
162 | tar_difficulty=difficulty)
163 | if db_similar2[1]:
164 | db_similar2[0].save()
165 | except Question.DoesNotExist:
166 | continue
167 |
168 |
169 | def get_detail(self, slug, paid_only, frequency):
170 | query_url = 'https://leetcode.com/graphql'
171 | current_headers = self.headers.copy()
172 | current_headers['referer'] = self.base_url + '/problems/' + slug
173 | current_headers['content-type'] = 'application/json'
174 | query = {'operationName': "getQuestionDetail",
175 | 'variables': {'titleSlug': slug},
176 | 'query': '''query getQuestionDetail($titleSlug: String!) {
177 | question(titleSlug: $titleSlug) {
178 | questionId
179 | questionFrontendId
180 | questionTitle
181 | questionTitleSlug
182 | content
183 | difficulty
184 | stats
185 | similarQuestions
186 | categoryTitle
187 | topicTags {
188 | name
189 | slug
190 | }
191 | # companyTags {
192 | # name
193 | # slug
194 | # }
195 | companyTagStats
196 | likes
197 | dislikes
198 | }
199 | }'''
200 | }
201 |
202 | detail = None
203 | retry_time = -1
204 | while detail is None and retry_time < self.max_retry_time:
205 | try:
206 | retry_time += 1
207 | if retry_time > 0:
208 | print(retry_time)
209 | detail = self.session.get(query_url, data=json.dumps(query), headers=current_headers).json()
210 | temp = detail['data']['question']
211 |
212 | except:
213 | pass
214 | self.save_to_db(detail, paid_only, frequency)
215 | print(temp['questionFrontendId'], temp['questionTitleSlug'])
216 |
217 | return detail
218 |
219 | # Multithreading version
220 | def get_all_details1(self):
221 | questions_list = self.get_questions_list()['stat_status_pairs']
222 | start = time.time()
223 | pool = multiprocessing.Pool()
224 | for question in questions_list:
225 | print(question['stat']['frontend_question_id'])
226 | paid_only = question['paid_only']
227 | frequency = question['frequency']
228 | if self.is_premium or not paid_only:
229 | pool.apply_async(self.get_detail, (question['stat']['question__title_slug'], paid_only, frequency))
230 | # self.getDetail(question['stat']['question__title_slug'])
231 | pool.close()
232 | pool.join()
233 | print("Multiple Processing Version: ", time.time() - start)
234 |
235 | # Sequential version
236 | def get_all_details2(self):
237 | questions_list = self.get_questions_list()['stat_status_pairs']
238 | start = time.time()
239 | for question in questions_list:
240 | paid_only = question['paid_only']
241 | frequency = question['frequency']
242 | if self.is_premium or not paid_only:
243 | self.get_detail(question['stat']['question__title_slug'], paid_only, frequency)
244 |
245 | print("Sequential Version: ", time.time() - start)
246 |
247 |
248 | if __name__ == '__main__':
249 | crawler = LeetCodeCrawler(1,2)
250 | # l = crawler.get_questions_list()
251 | # print(l)
252 | # crawler.login(1,2)
253 | # detail = crawler.get_detail('two-sum', False, 0)
254 | # detail = crawler.get_detail('3sum', False)
255 | # detail = crawler.get_detail('reordered-power-of-2', False)
256 |
257 | # print(detail['data']['question'])
258 | crawler.get_all_details1()
259 | # print(l['stat_status_pairs'][0])
260 |
--------------------------------------------------------------------------------