import re import logging import threading import time from flask import Flask, jsonify, request import netrc import json import asyncio from json.decoder import JSONDecodeError from urllib.request import urlopen from habanero import Crossref from waitress import serve from queue import Queue import search_bot NETRC_FILE = r"C:\Users\Activcom.pl\.netrc" HOST_ADDRESS = "192.168.1.192" PORT_ADDRESS = 5001 DATABASE_PATH = r'C:\\Database\\chunks' ENCODING = "utf-8" app = Flask(__name__) librarian_queue = Queue() class Librarian(object): def __init__(self, app, query, uuid) -> None: netrc_mod = netrc.netrc(NETRC_FILE) authTokens = netrc_mod.authenticators("crossref") self.cr = Crossref(mailto=authTokens[0]) self.query = query self.uuid = uuid self.limit = 1000 self.fetched = 0 self.hit = 0 self.total = 0 self.app = app async def search_crossref(self, query, uuid): self.app.logger.info("STARTED SEARCH") search_result_from_cr = self.cr.works(query = query, limit = self.limit) self.app.logger.info("CROSSREF DONE") self.total = search_result_from_cr['message']['total-results'] self.fetched = len(search_result_from_cr['message']['items']) #add to sql with UUID #sql row - title, doi, author, uuid, last retrieved, is_available with open("results.json", "r+", encoding="UTF-8") as data_file: # First we load existing data into a dict. try: file_data = json.load(data_file) except JSONDecodeError: file_data = [] summarized_results = [] self.app.logger.info("REFINE: Removing all derived works from the list") for item in search_result_from_cr['message']['items']: container = "NO" if "container-title" in item: container = "YES" summarized_results.append({"DOI" : item['DOI'], "title": item['title'] if 'title' in item else None,"type": item['type'] if 'type' in item else None, "container": container}) result = {"UUID": uuid, "total_results": search_result_from_cr['message']['total-results'], "on_page" : 1, "summary":summarized_results, "results":search_result_from_cr['message']['items'], } file_data.append(result) data_file.seek(0) self.app.logger.info("REFINE: Dumping to file") json.dump(file_data, data_file, indent=4) return result #TODO: check if exists odpala wątki dla kolejnych plików, z każdego wątku dostaje zwrotki - trafienia na bieżąco oraz sygnał że plik się skończył i pora na następny #TODO: paczki danych są upychane po 10 w tablicy, POSTy ze strony klienta mówią tylko nazwę tablicy (poprzez UUID) oraz którą stronę tejże #TODO: jeśli UUID nie istnieje to budujemy całość dla nowego uuid #TODO: kiedy wszystkie wątki pilokowe się zakończą - zamykamy merger i dajemy znać do procedury obsługi odpowiadania na posty że dla tego uuid już nic więcej nie znajdziemy #TODO: po 60 minutach storeujemy wyniki zapytania pod query w sql i kasujemy UUID #TODO: Później robimy tak że query jest najpierw sprawdzane w sql - i dawane jako "zapisana lokalna kopia" #TODO: Paginacja - najpierw hity z pliku, potem na końcu to co nie zwróciło żadnych hitów - i to dopiero można crawlerem sprawdzić async def check_dois_in_local_db(self, doi): self.app.logger.info("REFINE: Running search in the backend app") coro = asyncio.to_thread(search_bot.search_for_doi, doi) result = await coro return result async def check_if_exists_brute_force(self, page_url): self.app.logger.error("REFINE: Brute force search!") if not page_url.startswith(("http:", "https:")): raise ValueError("URL must start with 'http:' or 'https:'") # trunk-ignore(bandit/B310) with urlopen(page_url) as response: data = response.read() text = data.decode("utf-8") for line in text.splitlines(): if re.match(r".*Unfortunately, Sci-Hub doesn't have the requested document.*", line): return True if m := re.match(r".*