diff --git a/website/templates/company_detail.html b/website/templates/company_detail.html index 9c6c361..917dfc0 100644 --- a/website/templates/company_detail.html +++ b/website/templates/company_detail.html @@ -214,6 +214,11 @@

{{ job.role|default:job.title|d {% endif %} {% endif %}

+ {% if user.is_superuser %} + + + + {% endif %} {% if job.salary_min and job.salary_max %}

Salary: ${{ job.salary_min|floatformat:0|intcomma }} - ${{ job.salary_max|floatformat:0|intcomma }}

diff --git a/website/templates/partials/job_detail_include.html b/website/templates/partials/job_detail_include.html index 4e39761..b846f09 100644 --- a/website/templates/partials/job_detail_include.html +++ b/website/templates/partials/job_detail_include.html @@ -20,7 +20,7 @@

Applications (only visible to you)

actions link email - ping + ping {% for application in applications %} @@ -29,13 +29,7 @@

Applications (only visible to you)

{{ application.date_of_last_email|date:"m/d" }} {{ application.days_since_last_email }} - {{ application.email_set.count }} -
+ Posted - - {% if request.GET.show_stage %} - - Stage - - {% endif %} - @@ -244,13 +237,7 @@ {% if job.posted_date %}{{ job.posted_date|timesince }} ago{% endif %} - {% if request.GET.show_stage %} - - {% for application in job.application_set.all %} - {{ application.stage }} - {% endfor %} - - {% endif %} + @@ -267,12 +254,7 @@ {% if job.link %} {% if job.link_status_code == 200 %} - /", views.CompanyDetailView.as_view(), name="company_detail" - ), + #path( + # "company//", views.CompanyDetailView.as_view(), name="company_detail" + #), path("home/", views.home_view, name="home"), path("resume//", views.resume_view, name="resume"), path("mark-application-as-passed/", views.mark_application_as_passed, name="mark_application_as_passed"), path('model-counts/', views.model_counts_view, name='model-counts'), - path("company//add_job_link/", views.add_job_link, name="add_job_link"), path("add_email/", views.update_company_email, name="add_email"), path("dashboard/", DashboardView.as_view(), name="dashboard"), path(os.environ.get("ADMIN_URL", "admin/"), admin.site.urls), path("accounts/", include("allauth.urls")), - path("scrape-job/", views.scrape_job, name="scrape-job"), - # path("search/", views.search, name="search"), - path("job//", views.JobDetailView.as_view(), name="job_detail"), + path("scrape-job/", views.scrape_job, name="scrape-job"), + path("job//", CompanyJobDetailView.as_view(), name="job_detail", kwargs={"type": "job"}), + path("company//", CompanyJobDetailView.as_view(), name="company_detail", kwargs={"type": "company"}), path("privacy-policy/", views.privacy_policy, name="privacy_policy"), path("terms-of-service/", views.terms_of_service, name="terms_of_service"), path("favicon.ico", favicon_view), diff --git a/website/views.py b/website/views.py index 8e41e82..c81bf2e 100644 --- a/website/views.py +++ b/website/views.py @@ -772,44 +772,6 @@ def get_context_data(self, **kwargs): return context -# class CompanyListView(ListView): -# model = Company -# template_name = "company_list.html" -# context_object_name = "companies" -# paginate_by = 100 - -# def get_queryset(self): -# order = self.request.GET.get("order") - -# companies = cache.get("companies_queryset") -# if not companies: -# companies = Company.objects.prefetch_related("application_set") -# cache.set("companies_queryset", companies, 60 * 60 * 24) - -# if order: -# companies = companies.order_by(order) - -# return companies - - # def post(self, request, *args, **kwargs): - # company_id = request.POST.get("company_id") - # company = get_object_or_404(Company, id=company_id) - # form = CompanyUpdateForm(request.POST, instance=company) - - # if form.is_valid(): - # form.save() - - # order = request.POST.get("order") - # page = request.POST.get("page") - - # redirect_url = reverse("company_list") + "?" - # if order: - # redirect_url += f"order={order}&" - # if page: - # redirect_url += f"page={page}" - - # return HttpResponseRedirect(redirect_url) - def autocomplete(request, model): term = request.GET.get("term") @@ -912,7 +874,6 @@ def update_application_stage(request): application.stage = stage application.save() - # Invalidate cache for JobDetailView related to this application job_id = application.job.id user_id = request.user.id cache_key = f'job_detail_{job_id}_user_{user_id}' @@ -1308,120 +1269,220 @@ def post(self, request): return JsonResponse(data, status=status.HTTP_201_CREATED) +from django.core.cache import cache from django.shortcuts import get_object_or_404 +from django.utils import timezone from django.views.generic import DetailView +from requests.exceptions import RequestException -from .models import Application, Job +from .models import Application, Company, Job, Stage -class JobDetailView(DetailView): - model = Company - template_name = "company_detail.html" +class CompanyJobDetailView(DetailView): + template_name = "company_detail.html" # Single template for both - def get(self, request, *args, **kwargs): - slug = self.kwargs["slug"] - cache_key_company = f"cache_company_{slug}" - company = cache.get(cache_key_company) - - if not company: - job = get_object_or_404(Job, slug=slug) - company = job.company - cache.set(cache_key_company, company, timeout=60 * 60 * 24) # Cache for 24 hours + def get_object(self): + """ + Determine whether the slug is for a Job or Company based on the URL pattern. + """ + slug = self.kwargs.get("slug") + type = self.kwargs.get("type") + + if type == "job": + obj = get_object_or_404(Job, slug=slug) + elif type == "company": + obj = get_object_or_404(Company, slug=slug) else: - job = get_object_or_404(Job, slug=slug) + raise ValueError("Invalid type for URL") - # Cache key for the job details specific to the user - cache_key_job = f'job_detail_{job.id}_user_{self.request.user.id if self.request.user.is_authenticated else "anonymous"}' - cached_job_data = cache.get(cache_key_job) + return obj - if not cached_job_data: - cached_job_data = {} - cached_job_data["job"] = job + def get_common_data(self, obj): + """ + Fetch and cache common data like applications, stages, and company/job info. + """ + cache_key = f'detail_{obj.id}_user_{self.request.user.id if self.request.user.is_authenticated else "anonymous"}' + cached_data = cache.get(cache_key) - if self.request.user.is_authenticated: - applications = Application.objects.filter(job=job, user=self.request.user) - cached_job_data["applications"] = applications + if not cached_data: + cached_data = {"object": obj} + if isinstance(obj, Job): + cached_data["company"] = obj.company + else: + cached_data["company"] = obj - cached_job_data["stages"] = Stage.objects.annotate( - count=Count("application") - ).order_by("-order") + if self.request.user.is_authenticated: + if isinstance(obj, Job): + cached_data["applications"] = Application.objects.filter(job=obj, user=self.request.user) + else: + cached_data["applications"] = Application.objects.filter(company=obj, user=self.request.user) - cache.set(cache_key_job, cached_job_data, timeout=60 * 60 * 24 * 30) # Cache for 30 days + cached_data["stages"] = Stage.objects.annotate(count=Count("application")).order_by("-order") + cache.set(cache_key, cached_data, timeout=60 * 60 * 24 * 30) # Cache for 30 days - self.object = company + return cached_data - # Fetch and cache the next company + def get_next_company(self, company): next_company_cache_key = f"cache_company_{company.id + 1}" next_company = cache.get(next_company_cache_key) - if not next_company: - next_company = ( - Company.objects.filter(id__gt=company.id).first() - or Company.objects.all().order_by("?").first() - ) + next_company = Company.objects.filter(id__gt=company.id).first() or Company.objects.all().order_by("?").first() if next_company: - cache.set( - next_company_cache_key, next_company, timeout=60 * 60 * 24 - ) # Cache for 24 hours - - # Create the context and populate it - context = self.get_context_data(object=company) - context.update(cached_job_data) - context["next_company"] = next_company + cache.set(next_company_cache_key, next_company, timeout=60 * 60 * 24) # Cache for 24 hours + return next_company - # Fetch and cache website status + def update_website_status(self, company): website_status_cache_key = f"website_status_{company.id}" website_status_info = cache.get(website_status_cache_key) if not website_status_info: - if ( - not company.website_status_updated - or (datetime.now(timezone.utc) - company.website_status_updated).days >= 7 - ): - website = ( - company.website - if company.website - else f"https://{company.slug}.com" - ) + if not company.website_status_updated or (timezone.now() - company.website_status_updated).days >= 7: + website = company.website or f"https://{company.slug}.com" try: response = requests.get(website, timeout=10) company.website_status_updated = timezone.now() - - if ( - not company.website - and company.name.lower() in response.text.lower() - ): + if not company.website and company.name.lower() in response.text.lower(): company.website = response.url company.website_status = response.status_code except RequestException: company.website_status = 500 company.website_status_updated = timezone.now() finally: - company.save( - update_fields=[ - "website", - "website_status", - "website_status_updated", - ] - ) + company.save(update_fields=["website", "website_status", "website_status_updated"]) website_status_info = { "website_status": company.website_status, "website_status_updated": company.website_status_updated, "website": company.website, } - cache.set( - website_status_cache_key, - website_status_info, - timeout=60 * 60 * 24, - ) # Cache for 24 hours + cache.set(website_status_cache_key, website_status_info, timeout=60 * 60 * 24) + + return website_status_info + + def get_context_data(self, **kwargs): + obj = self.get_object() + context = super().get_context_data(**kwargs) + common_data = self.get_common_data(obj) + + context.update(common_data) + + if isinstance(obj, Job): + context["next_company"] = self.get_next_company(obj.company) + context["website_status_info"] = self.update_website_status(obj.company) + else: + context["next_company"] = self.get_next_company(obj) + context["website_status_info"] = self.update_website_status(obj) + + return context + + +# class JobDetailView(DetailView): +# model = Company +# template_name = "company_detail.html" + +# def get(self, request, *args, **kwargs): +# slug = self.kwargs["slug"] +# cache_key_company = f"cache_company_{slug}" +# company = cache.get(cache_key_company) + +# if not company: +# job = get_object_or_404(Job, slug=slug) +# company = job.company +# cache.set(cache_key_company, company, timeout=60 * 60 * 24) # Cache for 24 hours +# else: +# job = get_object_or_404(Job, slug=slug) + +# # Cache key for the job details specific to the user +# cache_key_job = f'job_detail_{job.id}_user_{self.request.user.id if self.request.user.is_authenticated else "anonymous"}' +# cached_job_data = cache.get(cache_key_job) - context["website_status_info"] = website_status_info +# if not cached_job_data: +# cached_job_data = {} +# cached_job_data["job"] = job - if not company.website and company.email: - company.website = f"https://{company.email.split('@')[1]}" - company.save(update_fields=["website"]) +# if self.request.user.is_authenticated: +# applications = Application.objects.filter(job=job, user=self.request.user) +# cached_job_data["applications"] = applications - return self.render_to_response(context) +# cached_job_data["stages"] = Stage.objects.annotate( +# count=Count("application") +# ).order_by("-order") + +# cache.set(cache_key_job, cached_job_data, timeout=60 * 60 * 24 * 30) # Cache for 30 days + +# self.object = company + +# # Fetch and cache the next company +# next_company_cache_key = f"cache_company_{company.id + 1}" +# next_company = cache.get(next_company_cache_key) + +# if not next_company: +# next_company = ( +# Company.objects.filter(id__gt=company.id).first() +# or Company.objects.all().order_by("?").first() +# ) +# if next_company: +# cache.set( +# next_company_cache_key, next_company, timeout=60 * 60 * 24 +# ) # Cache for 24 hours + +# # Create the context and populate it +# context = self.get_context_data(object=company) +# context.update(cached_job_data) +# context["next_company"] = next_company + +# # Fetch and cache website status +# website_status_cache_key = f"website_status_{company.id}" +# website_status_info = cache.get(website_status_cache_key) + +# if not website_status_info: +# if ( +# not company.website_status_updated +# or (datetime.now(timezone.utc) - company.website_status_updated).days >= 7 +# ): +# website = ( +# company.website +# if company.website +# else f"https://{company.slug}.com" +# ) +# try: +# response = requests.get(website, timeout=10) +# company.website_status_updated = timezone.now() + +# if ( +# not company.website +# and company.name.lower() in response.text.lower() +# ): +# company.website = response.url +# company.website_status = response.status_code +# except RequestException: +# company.website_status = 500 +# company.website_status_updated = timezone.now() +# finally: +# company.save( +# update_fields=[ +# "website", +# "website_status", +# "website_status_updated", +# ] +# ) +# website_status_info = { +# "website_status": company.website_status, +# "website_status_updated": company.website_status_updated, +# "website": company.website, +# } +# cache.set( +# website_status_cache_key, +# website_status_info, +# timeout=60 * 60 * 24, +# ) # Cache for 24 hours + +# context["website_status_info"] = website_status_info + +# if not company.website and company.email: +# company.website = f"https://{company.email.split('@')[1]}" +# company.save(update_fields=["website"]) + +# return self.render_to_response(context) def privacy_policy(request): @@ -1596,203 +1657,96 @@ def get_context_data(self, **kwargs): return context +# class CompanyDetailView(generic.DetailView): +# model = Company +# template_name = "company_detail.html" +# def get(self, request, *args, **kwargs): +# slug = self.kwargs["slug"] +# cache_key = f"cache_company_{slug}" +# company = cache.get(cache_key) +# if not company: +# company = get_object_or_404(self.get_queryset().select_related(), slug=slug) +# cache.set(cache_key, company, timeout=60 * 60 * 24) # Cache for 24 hours +# self.object = company -# @method_decorator(vary_on_cookie, name='dispatch') -# @method_decorator(cache_page(60 * 60 * 24), name='dispatch') # cache for 1 day -# class Index(TemplateView): -# template_name = "index.html" - -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) -# all_companies = Company.objects.all() -# #companies = list(all_companies) -# #random.shuffle(companies) -# #context["companies"] = companies[:50] -# context["company_count"] = all_companies.count() -# context["job_count"] = Job.objects.all().count() -# context["sources_count"] = Source.objects.all().count() -# context["sessions_count"] = Session.objects.all().count() - -# time_threshold = timezone.now() - timedelta(hours=24) +# # Fetch and cache next company using the same cache key pattern +# next_company_cache_key = f"cache_company_{company.id + 1}" +# next_company = cache.get(next_company_cache_key) -# # Annotate each user with the count of their applications and applications in the last 24 hours -# users_with_counts = User.objects.annotate( -# total_applications=Count('application'), -# applications_last_24hr=Count( -# 'application', -# filter=Q(application__created__gte=time_threshold) +# if not next_company: +# next_company = ( +# Company.objects.filter(id__gt=company.id).first() +# or Company.objects.all().order_by("?").first() # ) -# ) - -# # # Build list of dictionaries -# # user_applications = [ -# # { -# # 'total_applications': user.total_applications, -# # 'applications_last_24hr': user.applications_last_24hr, -# # } -# # for user in users_with_counts -# # ] - -# # # Sort by total_applications, highest to lowest -# # user_applications.sort(key=lambda x: x['total_applications'], reverse=True) - -# # context["user_applications"] = user_applications[:3] - -# return context - - -class CompanyDetailView(generic.DetailView): - model = Company - template_name = "company_detail.html" - - def get(self, request, *args, **kwargs): - slug = self.kwargs["slug"] - cache_key = f"cache_company_{slug}" - company = cache.get(cache_key) - - if not company: - company = get_object_or_404(self.get_queryset().select_related(), slug=slug) - cache.set(cache_key, company, timeout=60 * 60 * 24) # Cache for 24 hours - - self.object = company - - # Fetch and cache next company using the same cache key pattern - next_company_cache_key = f"cache_company_{company.id + 1}" - next_company = cache.get(next_company_cache_key) - - if not next_company: - next_company = ( - Company.objects.filter(id__gt=company.id).first() - or Company.objects.all().order_by("?").first() - ) - if next_company: - cache.set( - next_company_cache_key, next_company, timeout=60 * 60 * 24 - ) # Cache for 24 hours - - context = self.get_context_data(object=company) - context["next_company"] = next_company - if request.user.is_authenticated: - context["applications"] = Application.objects.filter( - user=request.user, company=company - ) - context["stages"] = Stage.objects.all().order_by("-order") - - - # Fetch and cache website status - website_status_cache_key = f"website_status_{company.id}" - website_status_info = cache.get(website_status_cache_key) - - if not website_status_info: - if ( - not company.website_status_updated - or (datetime.now(timezone.utc) - company.website_status_updated).days - >= 7 - ): - website = ( - company.website - if company.website - else f"https://{company.slug}.com" - ) - try: - response = requests.get(website, timeout=10) - company.website_status_updated = timezone.now() - - if ( - not company.website - and company.name.lower() in response.text.lower() - ): - company.website = response.url - company.website_status = response.status_code - except RequestException: - company.website_status = 500 - company.website_status_updated = timezone.now() - finally: - company.save( - update_fields=[ - "website", - "website_status", - "website_status_updated", - ] - ) - website_status_info = { - "website_status": company.website_status, - "website_status_updated": company.website_status_updated, - "website": company.website, - } - cache.set( - website_status_cache_key, - website_status_info, - timeout=60 * 60 * 24, - ) # Cache for 24 hours - - if not company.website and company.email: - company.website = f"https://{company.email.split('@')[1]}" - company.save(update_fields=["website"]) - - return self.render_to_response(context) - - # disable screenshot code until we can get it working on render - # if company.website: - # from selenium.webdriver.chrome.service import Service - # logger.info('getting screenshot') - # #service = Service(executable_path=ChromeDriverManager().install()) - - # service = Service(executable_path="/opt/render/project/.render/chrome/chromedriver") - - # logger.info('setting options') - # options = webdriver.ChromeOptions() - # options.binary_location = "/opt/render/project/.render/chrome/opt/google/chrome/google-chrome" - # options.page_load_strategy = 'eager' - # options.add_argument("--headless") # Ensure GUI is off - # options.add_argument("--no-sandbox") - # options.add_argument("--disable-dev-shm-usage") - # options.add_argument("--window-size=1920,1080") # Set window size to standard desktop size - # options.add_argument("--hide-scrollbars") # Hide scrollbars on screenshot - - # logger.info('getting browser') - # browser = webdriver.Chrome(service=service, options=options) - # browser.set_page_load_timeout(10) - - # logger.info('getting url') - # logger.info(company.website) - - # url = company.website # The URL you want to take a screenshot of - # try: - # browser.get(url) - # logger.info('getting screenshot') - - # screenshot = browser.get_screenshot_as_png() - - # file_name = f"screenshot_{company.slug}.png" - # company.screenshot.save(file_name, ContentFile(screenshot), save=True) - - # except TimeoutException: - # logger.error(f"Timeout exceeded for URL {url}") - - # browser.quit() - - -def add_job_link(request, slug): - if request.method == "POST": - company = get_object_or_404(Company, slug=slug) - job_link = request.POST.get("job_link") - job_link = job_link.strip() - # check that job_link contains greenhouse or lever or return error - if "lever" not in job_link and "greenhouse" not in job_link: - return render( - request, - "company_detail.html", - {"company": company, "error": "Invalid job link"}, - ) - - job = Job(link=job_link, company=company) - job.save() - return redirect("company_detail", slug=slug) - +# if next_company: +# cache.set( +# next_company_cache_key, next_company, timeout=60 * 60 * 24 +# ) # Cache for 24 hours + +# context = self.get_context_data(object=company) +# context["next_company"] = next_company +# if request.user.is_authenticated: +# context["applications"] = Application.objects.filter( +# user=request.user, company=company +# ) +# context["stages"] = Stage.objects.all().order_by("-order") + + +# # Fetch and cache website status +# website_status_cache_key = f"website_status_{company.id}" +# website_status_info = cache.get(website_status_cache_key) + +# if not website_status_info: +# if ( +# not company.website_status_updated +# or (datetime.now(timezone.utc) - company.website_status_updated).days +# >= 7 +# ): +# website = ( +# company.website +# if company.website +# else f"https://{company.slug}.com" +# ) +# try: +# response = requests.get(website, timeout=10) +# company.website_status_updated = timezone.now() + +# if ( +# not company.website +# and company.name.lower() in response.text.lower() +# ): +# company.website = response.url +# company.website_status = response.status_code +# except RequestException: +# company.website_status = 500 +# company.website_status_updated = timezone.now() +# finally: +# company.save( +# update_fields=[ +# "website", +# "website_status", +# "website_status_updated", +# ] +# ) +# website_status_info = { +# "website_status": company.website_status, +# "website_status_updated": company.website_status_updated, +# "website": company.website, +# } +# cache.set( +# website_status_cache_key, +# website_status_info, +# timeout=60 * 60 * 24, +# ) # Cache for 24 hours + +# if not company.website and company.email: +# company.website = f"https://{company.email.split('@')[1]}" +# company.save(update_fields=["website"]) + +# return self.render_to_response(context) from urllib.parse import urlparse