# Ben M, Travis Z, Andy P
# GUI implementation of Software engineering program
# sprint 2
# last edited: 3/2/22

# https://www.youtube.com/watch?v=H3Cjtm6NuaQ

# =================================================================================================================================================================================================================================================================================

from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
import tkinter as tk
from tkinter import ttk

from classModel import *
from relationshipsModel import *
from saveLoadModel import *
from attributesModel import *
from interfaceView import *
from parametersModel import *
from sharedItems import *

# =================================================================================================================================================================================================================================================================================

# initialize window
window = tk.Tk()
window.title("bootsnake")
window.geometry("400x200")

# =================================================================================================================================================================================================================================================================================

# list for entries in entry boxes
entries = []

# =================================================================================================================================================================================================================================================================================

# variables to keep track of drop down menu, and lists for drop down options
classVar = StringVar(window)
classOptions = [
    '',
    'Add',
    'Delete',
    'Rename',
]
relationshipVar = StringVar(window)
relationshipOptions = [
    '',
    'Add',
    'Delete',
    'Edit',
]
methodsVar = StringVar(window)
methodsOptions = [
    '',
    'Add',
    'Delete',
    'Rename',
]
fieldsVar = StringVar(window)
fieldsOptions = [
    '',
    'Add',
    'Delete',
    'Rename',
]
parameterVar = StringVar(window)
parameterOptions = [
    '',
    'Add',
    'Change',
]

# =================================================================================================================================================================================================================================================================================

# calls for each file's methods
def classCalls(*args):
    err = False
    if classVar.get() == 'Add':
        windowEntry(1, "Class Add")
        err = ClassAdd(entries[0])
    elif classVar.get() == 'Delete':
        windowEntry(1, "Class Delete")
        err = ClassDelete(entries[0])
    elif classVar.get() == 'Rename':
        windowEntry(2, "Class Rename")
        err = ClassRename(entries[0], entries[1])
    if err is not False:
        throwMessage(err)
    classVar.set("")

def relationshipCalls(*args):
    err = False
    if relationshipVar.get() == 'Add':
        windowEntry(3, "Relationship Add")
        if entries[2] == "Aggregation" or entries[2] == "Composition" or entries[2] == "Inheritance" or entries[2] == "Realization":
            err = RelationshipAdd(entries[0], entries[1], entries[2])
        else:
            throwMessage("Incorrect relationship type. The available options are: Aggregation, Composition, Inheritance, Realization")
            return
    elif relationshipVar.get() == 'Delete':
        windowEntry(2, "Relationship Delete")
        err = RelationshipDelete(entries[0], entries[1])
    elif relationshipVar.get() == 'Edit':
        windowEntry(3, "Relationship Edit")
        print(entries[2])
        if entries[2] == "Aggregation" or entries[2] == "Composition" or entries[2] == "Inheritance" or entries[2] == "Realization":
            err = relationshipEdit(entries[0], entries[1], entries[2])
        else:
            throwMessage("Incorrect relationship type. The available options are: Aggregation, Composition, Inheritance, Realization")
            return
    if err is not False:
        throwMessage(err)
    relationshipVar.set("")

def methodCalls(*args):
    err = False
    if methodsVar.get() == 'Add':
        windowEntry(2, "Method Add")
        err = addMethod(entries[0], entries[1], [])
    elif methodsVar.get() == 'Delete':
        windowEntry(3, "Method Delete")
        err = delMethod(entries[0], entries[1])
    elif methodsVar.get() == 'Rename':
        windowEntry(2, "Method Rename")
        err = renMethod(entries[0], entries[1], entries[2])
    if err is not False:
        throwMessage(err)
    methodsVar.set("")

def fieldCalls(*args):
    err = False
    if fieldsVar.get() == 'Add':
        windowEntry(2, "Field Add")
        err = addField(entries[0], entries[1])
    elif fieldsVar.get() == 'Delete':
        windowEntry(2, "Field Delete")
        err = delField(entries[0], entries[1])
    elif fieldsVar.get() == 'Rename':
        windowEntry(3, "Field Rename")
        err = renField(entries[0], entries[1], entries[2])
    if err is not False:
        throwMessage(err)
    fieldsVar.set("")

def parameterCalls(*args):
    err = False
    if parameterVar.get() == 'Add':
        windowEntry(4, "Parameter Add")
        err = ParamAdd(entries[0], entries[1], entries[2], entries[3])
    elif parameterVar.get() == 'Change':
        windowEntry(3, "Parameter Change")
        err = changeParam(entries[0], entries[1], entries[2])
    if err is not False:
        throwMessage(err)
    parameterVar.set("")

def saveCall():
    err = False
    filename = filedialog.asksaveasfilename()
    if filename != "":
        err= save(filename)
    if err is not False:
        throwMessage(err)

def loadCall():
    err = False
    #filename = filedialog.askopenfilename()
    filename = filedialog.askopenfilename(filetypes=(("Json File", "*.json"),), title="Choose JSON file")
    if filename != "":
        err = load(filename)
    if err is not False:
        throwMessage(err)

def listClassCall():
    windowEntry(1, "List Class")
    message = ListClass(entries[0])
    throwMessage(message)

def listClassesCall():
    message = ListClasses()
    throwMessage(message)

def listRelationshipsCall():
    message = ListRelationships()
    throwMessage(message)

# =================================================================================================================================================================================================================================================================================

# for making an arbitrary number of entry boxes for functions
# makes a new window and adds entry boxes.
# updates entries list with those entry box objects to be accessed by other functions
def windowEntry(numEntries: int, title: str):

    # create new pop up window
    newWindow = Toplevel(window)
    newWindow.title(title)
    newWindow.geometry("400x200")

    # create start button and entry boxes based on input
    startVar = tk.IntVar()
    startButton = Button(newWindow, text='Go!', command=lambda: startVar.set(1))
    startButton.grid(row=1, column=0, pady=20)

    entryObjList = []
    for x in range(numEntries):
        entry = Entry(newWindow)
        entry.grid(row=0, column=x, pady=20, padx=5)
        entryObjList.append(entry)

    # waits for button to be pressed before returning to function
    startButton.wait_variable(startVar)

    # once button has been pressed, add string entries to list for usage. this means we can now close the window
    entries.clear()
    for e in entryObjList:
        entries.append(e.get())

    # close window automatically, we cant close until we are done with the entries, so we may not be able to do this with this current implementation
    newWindow.destroy()

# make a new message with given string
def throwMessage(mes: str):
    messagebox.showinfo("Message", mes)

# =================================================================================================================================================================================================================================================================================

# drop down menu and label objects
classLabel = tk.Label(window, text="Class").grid(row=0, column=0)
classDrop = tk.OptionMenu(window, classVar, *classOptions).grid(row=1, column=0)
#classDrop = ttk.Combobox(window, classOptions).grid(row=1, column=0)

relationshipLabel = tk.Label(window, text="Relationship").grid(row=0, column=1)
relationshipDrop = tk.OptionMenu(window, relationshipVar, *relationshipOptions).grid(row=1, column=1)

fieldsLabel = tk.Label(window, text="Fields").grid(row=0, column=2)
fieldsDrop = tk.OptionMenu(window, fieldsVar, *fieldsOptions).grid(row=1, column=2)

methodsLabel = tk.Label(window, text="Methods").grid(row=0, column=3)
methodsDrop = tk.OptionMenu(window, methodsVar, *methodsOptions).grid(row=1, column=3)

parameterLabel = tk.Label(window, text="Paramters").grid(row=0, column=4)
parameterDrop = tk.OptionMenu(window, parameterVar, *parameterOptions).grid(row=1, column=4)

listClassButton = tk.Button(window, text="List Class", command=listClassCall).grid(row=2, column=0)
listClassesButton = tk.Button(window, text="List Classes", command=listClassesCall).grid(row=2, column=1)
listRelationshipsButton = tk.Button(window, text="List Relationships", command=listRelationshipsCall).grid(row=2, column=2)

saveButton = tk.Button(window, text="Save", command=saveCall).grid(row=3, column=0)
loadButton = tk.Button(window, text="Load", command=loadCall).grid(row=3, column=1)

# =================================================================================================================================================================================================================================================================================

# keeping track of whether a drop down menu option was selected
classVar.trace("w", classCalls)
relationshipVar.trace("w", relationshipCalls)
methodsVar.trace("w", methodCalls)
fieldsVar.trace("w", fieldCalls)
parameterVar.trace("w", parameterCalls)

# =================================================================================================================================================================================================================================================================================

# end
window.mainloop()


"""api"""
from typing import Dict, List
import json
from fastapi import FastAPI
import uvicorn
from pydantic import BaseModel
import transformer_model
import keywords_grading
import manual_keywords_grading
import siamese_ner_grading
import  plagiarism_model
from api_utils import dict_to_list
from configs import configs as cfg

DEBUGGING = cfg["debugging"]
if not DEBUGGING:
    import warnings
    warnings.filterwarnings('ignore')

BERT = transformer_model.BERTModel()
# grading models
SNGM = siamese_ner_grading.SIAMESENERGradingModel(BERT)
KGM = keywords_grading.KeywordsGradingModel(BERT)
MKGM = manual_keywords_grading.ManualKeywordsGradingModel(BERT)
MODELS = { "siamese_ner_grading": SNGM, "automatic_keywords_grading": KGM, "manual_keywords_grading": MKGM }
# plagiarism model
PM = plagiarism_model.PlagiarismModel(BERT)

app = FastAPI(
    title="automatic Essay Grading and Pligarism check Models API",
    description="An API that use NLP models to return the plagiarism , grading result",
    version="2.0.1",
)

class StudentsDict(BaseModel):
    """
    StudentsDict input model validation

    Args:
        BaseModel : inherit from pydantic BaseModel to validate the input

    Attributes:
        students_dict (dict): students_dict
            contains dicts of the student's answers and their ids
        cased (bool): cased strings grading or not

    example:
        >>> "students_dict": {
        >>>  "1235dx":
        >>>        "answer_1"
        >>>    "24463dxcf":
        >>>         "answer_2"
        >>> }
        >>> "cased": False
    """
    students_dict: Dict[str, str]
    cased: bool = False
class ESSAYSDICT(BaseModel):
    """
    ESSAYSDICT input model validation

    Args:
        BaseModel : inherit from pydantic BaseModel to validate the input

    Attributes:
        essays_dict (dict): essays_dict
            contains dicts of the student's answers and their ids
        grading_model (str): grading_model
            grading model to use choose from ["siamese_ner_grading",
                "keywords_grading" , "manual_keywords_grading"]
        cased (bool): cased strings grading or not

    example:
        >>> "essays_dict": {
        >>>  "1235dx":
        >>>        "answer_1"
        >>>    "24463dxcf":
        >>>         "answer_2"
        >>> }
        >>> "grading_model": "keywords_grading"
        >>> "cased": False
    """
    essays_dict: Dict[str, str]
    grading_model: str = "siamese_ner_grading"
    cased: bool = False

    # validation of the grading_model to have only the 3 options
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.grading_model not in ["siamese_ner_grading", "automatic_keywords_grading",
             "manual_keywords_grading"]:
            raise ValueError("grading_model must be one of the following: \
                siamese_ner_grading, automatic_keywords_grading, manual_keywords_grading")
class PlagiarismResponse(BaseModel):
    """
    PlagiarismResponse output model validation
        it's a json string contains a List of Dictionaries of str [ids] : float [scores]

    Args:
        BaseModel : inherit from pydantic BaseModel to validate the output

    Attributes:
        scores (List[Dict[str, float]]): scores
            contains a list of dicts of the student's answers and their ids

    example:
        >>> {
        >>>     "ids": ["1", "2", "3"],
        >>>     "scores": [0.5, 0.5, 0.5]
        >>> }
    """
    plagiarism_results : List[ Dict[str, Dict[str,float]]]
class GradingResponse(BaseModel):
    """
    StudentsDict output model validation
        it's a json string contains a List of Dictionaries of str [ids] : float [scores]

    Args:
        BaseModel : inherit from pydantic BaseModel to validate the output

    Attributes:
        grades ([Dict[str, float]]): scores
            contains a list of dicts of the student's grades and their ids

    example:
        >>> "students_dict": {
        >>>  "1235dx":
        >>>        "answer_1"
        >>>    "24463dxcf":
        >>>         "answer_2"
        >>> }

        >>> "grades":
        >>>     {
        >>>         "7818ert": 1,
        >>>         "4581rdc": 0.5,
        >>>     }

    """
    grades : Dict[str,float]

@app.get(path="/")
def read_root():
    """
    read index (root)
    for testing purpose

    Args:
        query (str): query string

    Returns:
        str: \"message\": hi
    """
    return json.dumps({"message": "Hi"})

@app.post("/grading/predict", response_model=GradingResponse)
def predict_grad(answers: ESSAYSDICT)\
     -> GradingResponse:
    """
    Predict the grading result

    Args
    ----------
    answers : dict[str,str]
        dict of student ids and their answers
        first element is the model answer
        second etc... elements are the students answers

    Returns
    -------
    Dict[str,float]
        dict of studetns ids and their grades

    example
    -------
        >>> predict_grad("essays_dict":
        >>> {"7821": "ahmed is eating a pizza",
        >>> "156": "i go to school by bus",
        >>> "3": "i am eating a pizza",
        >>> "4": "ahmed is eating a pizza"}
        >>>  "grading_model": "automatic_keywords_grading")

        "grades": {
            "156": 0.028096482157707214,
            "3": 0.45442324082056684,
            "4": 0.9999998410542806
                }
    """
    model = MODELS[answers.grading_model]
    ids, answers = dict_to_list(answers.essays_dict, answers.cased)
    res = model.predict(ids=ids, answers=answers)
    return {"grades":res}

@app.post("/plagiarism/predict", response_model=PlagiarismResponse)
def predict_plagiarism(students_dict: StudentsDict)\
    -> List[ Dict[str, Dict[str,float]]]:
    """
    Predict the pligarism result

    Args
    ----------
    students_dict : dict[str,str]
        dict of student ids and their answers

    Returns
    -------
    dict[str,dict[str,float]]
        dict of cheating studetns ids
        and their probabilities of plagiarism with other students

    example
    -------
    >>> predict_plagiarism('{1:\"Hello World\", 2:\"Hello World\"}, 3:\"hi there\"},cased: False')
    >>> [{1: {2: 0.98}, 2: {1: 0.98}}]

    """
    ids, answers = dict_to_list(students_dict.students_dict, students_dict.cased)
    res = PM.predict(answers, ids)
    if DEBUGGING:
        print(res)
    return {"plagiarism_results":res}

if __name__ == '__main__':

    if not DEBUGGING:
        print("DEBUGGING is ON")
        uvicorn.run(app, host='127.0.0.1', port=8000)
    else:
        uvicorn.run(app, host='0.0.0.0', port=8080)


import jwt
from ninja.security import HttpBearer
from django.conf import settings

class GlobalAuhentication(HttpBearer):
    def authenticate(self, request, token):
        try:
            JWT_SIGNING_KEY = getattr(settings, 'JWT_SIGNING_KEY', None)
            payload = jwt.decode(token, JWT_SIGNING_KEY, algorithms=['HS256'])
            username: str = payload.get('sub')
            if username is None:
                return None
        except jwt.PyJWTError:
            return None

        return username


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Nov 20 10:01:45 2022

@author: arek

It's example file NOT a part of module!
"""

# %%
# %%
# app/xyz.py
import argparse

INFO= """..."""

def get_parser():

    parser = argparse.ArgumentParser(
        description = INFO,
        formatter_class = lambda prog: argparse.HelpFormatter(
            prog, max_help_position=60, width=110)
    )

    parser.add_argument("--arg", default=None, type=int, help="...")
    parser.add_argument("--arg2", default=None, type=str, help="...")

    return parser

def get_args():
    parser = get_parser()
    print(INFO)

    args = parser.parse_args()

    # args processing -- should be wrapped in as function
    args.new = 1
    args.new2 = f"{args.arg}"
    # ...

    return args

def main(args):
    result = []
    # result.append(f(args.arg))
    # result.append(g(args.arg2, arg.new))
    # result.append(h(arg.new2))
    # ...
    return result

#%%
if __name__ == '__main__':
    args = get_args()
    main(args)

# %%
# %%
# common/testutils/testutils.py
def parse_with(parser, params, *flags):
    """
    params = {k: v, k1: v0, ...}
    flags = ["k", "k0", ...]
    """
    res = []
    for k, v in params.items():
        res.append(f'--{k}')
        res.append(f'{str(v)}')
    for k in flags:
        res.append(f'--{k}')
    return parser.parse_args(res)

# %%
# %%
# tests/test_xyz.y
from app.xyz import main, get_parser
from common.testutils import parse_with

def test_xyz():
    parser = get_parser()
    args = parse_with(parser,
               {"arg": 1,
                "arg2": "qq"},
               )

    # args processing -- should be wrapped in as function
    args.new = 1
    args.new2 = f"{args.arg}"
    # ...

    main(args)

    # ...


from django.db.models import Avg
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, viewsets
from rest_framework.pagination import LimitOffsetPagination

from api.filters import TitleFilter
from api.permissions import IsAdminOrReadOnly, IsAuthorOrStaffOrReadOnly
from api.serializers import (CategorySerializer, CommentSerializer,
                             GenreSerializer, ReviewSerializer,
                             TitleCreateSerializer, TitleListSerializer)
from reviews.models import Category, Genre, Review, Title
from utils.mixins import ListCreateDestroyViewSet, MultiSerializerViewSetMixin

class ReviewViewSet(viewsets.ModelViewSet):
    serializer_class = ReviewSerializer
    permission_classes = [IsAuthorOrStaffOrReadOnly]

    def get_queryset(self):
        title = get_object_or_404(
            Title,
            id=self.kwargs.get('title_id')
        )
        return title.reviews.all()

    def perform_create(self, serializer):
        title = get_object_or_404(
            Title,
            id=self.kwargs.get('title_id')
        )
        serializer.save(
            author=self.request.user,
            title=title
        )

class CommentViewSet(viewsets.ModelViewSet):
    serializer_class = CommentSerializer
    permission_classes = [IsAuthorOrStaffOrReadOnly]

    def get_queryset(self):
        review = get_object_or_404(
            Review,
            id=self.kwargs.get('review_id')
        )
        return review.comments.all()

    def perform_create(self, serializer):
        title_id = self.kwargs.get('title_id')
        review_id = self.kwargs.get('review_id')
        review = get_object_or_404(
            Review.objects.filter(title_id=title_id),
            pk=review_id
        )
        serializer.save(
            author=self.request.user,
            review=review
        )

class TitleViewSet(MultiSerializerViewSetMixin, viewsets.ModelViewSet):
    queryset = Title.objects.annotate(
        rating=Avg('reviews__score')
    )
    filterset_class = TitleFilter
    permission_classes = [IsAdminOrReadOnly]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    pagination_class = LimitOffsetPagination
    search_fields = ('name', 'year', 'genre__slug', 'category__slug')
    ordering = ('name',)
    serialier_class = TitleListSerializer
    serializer_action_classes = {
        'list': TitleListSerializer,
        'create': TitleCreateSerializer,
        'retrieve': TitleListSerializer,
        'update': TitleCreateSerializer,
        'partial_update': TitleCreateSerializer
    }

class GenreViewSet(ListCreateDestroyViewSet):
    queryset = Genre.objects.all()
    serializer_class = GenreSerializer
    permission_classes = [IsAdminOrReadOnly, ]
    pagination_class = LimitOffsetPagination
    filter_backends = [DjangoFilterBackend, filters.SearchFilter]
    search_fields = ['=name', ]
    lookup_field = ('slug')

class CategoryViewSet(ListCreateDestroyViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    permission_classes = [IsAdminOrReadOnly, ]
    pagination_class = LimitOffsetPagination
    filter_backends = [filters.SearchFilter]
    search_fields = ['=name', ]
    lookup_field = 'slug'


#!/usr/bin/env python3

import sys

contexts = 0
for line in sys.stdin.buffer:
    if line.startswith(b'ngram'):
        contexts += int(line.strip().split(b'=')[1])
    elif b'gram' in line:
        break

print("{}".format(contexts))


r"""
======================================================
B201: Test for use of flask app with debug set to true
======================================================

Running Flask applications in debug mode results in the Werkzeug debugger
being enabled. This includes a feature that allows arbitrary code execution.
Documentation for both Flask [1]_ and Werkzeug [2]_ strongly suggests that
debug mode should never be enabled on production systems.

Operating a production server with debug mode enabled was the probable cause
of the Patreon breach in 2015 [3]_.

:Example:

.. code-block:: none

    >> Issue: A Flask app appears to be run with debug=True, which exposes
    the Werkzeug debugger and allows the execution of arbitrary code.
       Severity: High   Confidence: High
          Location: examples/flask_debug.py:10
          9 #bad
          10    app.run(debug=True)
          11

.. seealso::

 .. [1] http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode
 .. [2] http://werkzeug.palletsprojects.com/en/0.15.x/debug/
 .. [3] http://labs.detectify.com/post/130332638391/how-patreon-got-hacked-publicly-exposed-werkzeug

.. versionadded:: 0.15.0

"""  # noqa: E501

import bandit
from bandit.core import test_properties as test

@test.test_id('B201')
@test.checks('Call')
def flask_debug_true(context):
    if context.is_module_imported_like('flask'):
        if context.call_function_name_qual.endswith('.run'):
            if context.check_call_arg_value('debug', 'True'):
                return bandit.Issue(
                    severity=bandit.HIGH,
                    confidence=bandit.MEDIUM,
                    text="A Flask app appears to be run with debug=True, "
                         "which exposes the Werkzeug debugger and allows "
                         "the execution of arbitrary code.",
                    lineno=context.get_lineno_for_call_arg('debug'),
                )


from __future__ import annotations

from promptware.constants import (
    ApplicationCategory,
    ApplicationSubcategory,
    DesignPatternType,
    LanguageType,
    LicenseType,
    PlatformType,
    TaskType,
)
from promptware.info import SoftwareInfo
from promptware.kernels.plm import PLMKernelConfig
from promptware.promptware import PromptConfig, Promptware

class QueryDocMatchingPromptware(Promptware):
    def _info(self) -> SoftwareInfo:
        return SoftwareInfo(
            description="This software is to identify if a query semantically "
            "relevant to a document.",
            creator="Promptware Authors",
            homepage="https://github.com/expressai/promptware",
            reference="",
            codebase_url="https://github.com/expressai/promptware/tree/main/softwares",
            license=LicenseType.apache_2_0,
            research_tasks=[TaskType.text_pair_classification],
            application_categories=[ApplicationCategory.classification],
            application_subcategories=[ApplicationSubcategory.general_classification],
            original_platform=PlatformType.gpt3,
            design_pattern=DesignPatternType.standalone,
            source_language=LanguageType.en,
            target_language=LanguageType.en,
        )

    def _kernel_configs(self):
        return {
            "openai": PLMKernelConfig(
                platform="openai",
                model_name="text-davinci-002",
                max_tokens=64,
                temperature=0,
            )
        }

    def _software_configs(self):
        return {
            "query_doc_matching": PromptConfig(
                name="query_doc_matching",
                description="This software is to identify if a query semantically "
                "relevant to a document.",
                instruction="Decide whether the documents are relevant to the given "
                "search query:\n",
                demonstration=[
                    "Query: Jackie Chan's birthday\nDocuments: "
                    "https://en.wikipedia.org/wiki/Jackie_Chan|Jackie Chan - "
                    "Wikipedia|Fang Shilong (born 7 April 1954), known professionally "
                    "in English as Jackie Chan and in Chinese as Cheng Long, is a Hong"
                    " Kong actor, filmmaker, "
                    "martial artist, and stuntman known for his slapstick acrobatic "
                    "fighting style, comic timing, and innovative stunts, which he "
                    "typically performs himself. Chan has been acting since the "
                    "1960s, performing in more than 150 films. He is one of the most "
                    "popular action film stars of all time.\nResult: Relevant\n",
                    "Query:Pengfei Liu in "
                    "NLP\nDocuments:https://scholar.google.com/citations?user"
                    "=oIz_CYEAAAAJ&hl=en|Pengfei Liu - Google Scholar|Carnegie "
                    "Mellon University -Natural Language Processing - Text "
                    "Summarization - Diagnosis for NLP Models - "
                    "Multi-task/Transfer\nResult: Relevant\n",
                    "Query: Chinese New Year 2023\nDocuments: "
                    "https://en.wikipedia.org/wiki/Tang_dynasty|Tang dynasty - "
                    "Wikipedia| The Tang dynasty, or Tang Empire, was an imperial "
                    "dynasty of China that ruled from 618 to 907 AD, with an "
                    "interregnum between 690 and 705. It was preceded by the Sui "
                    "dynasty and followed by the Five Dynasties and Ten Kingdoms "
                    "period.\nResult:Not relevant\n",
                ],
                prompt_template=lambda input: f"Query: {input['query']}\nDocuments:"
                f"{input['document']['link']} | "
                f"{input['document']['title']}"
                f" | {input['document']['document']}"
                f"\nResult: ",
                task=TaskType.text_pair_classification,
            )
        }

    def _example(self):
        return {
            "input": {
                "query": "Zhiyuan Liu address",
                "document": {
                    "title": "Zhiyuan Liu",
                    "link": "https://nlp.csai.tsinghua.edu.cn/~lzy/",
                    "document": " I'm an associate professor at Tsinghua "
                    "University. I am "
                    "always looking for highly-motivated post-docs "
                    "and "
                    "visiting scholars to work together on natural "
                    "language "
                    "processing, knowledge graphs, and social "
                    "computing. "
                    "Please read the Natural Language Processing. "
                    "Language: "
                    "English. The degree course of CS graduate "
                    "students ("
                    "specially for the Master Program of Advanced "
                    "Programming) at Tsinghua University. [ "
                    "Foundations of "
                    "Object-Oriented Programming. Language: Chinese. "
                    "The "
                    "compulsory course of CS undergraduate students "
                    "at "
                    "Tsinghua University.",
                },
            },
            "output": "",
        }


input = []
with open('inputs/day23.input','r') as f:
    for line in f:
        l = line.strip('\n')
        c = l.split()
        cmd = []
        for x in c:
            try:
                cmd.append(int(x))
            except ValueError:
                cmd.append(x)
        input.append(cmd)

r = {'a': 0, 'b': 0, 'c': 0, "d": 0, "e": 0, "f": 0, "g": 0, "h": 0}

def getValue(v):
    try:
        return int(v)

    except ValueError:
        return r[v]

count = 0
cur = 0

steps = 0
while (0  <= cur < len(input)):
    cmd = input[cur]
    c = cmd[0]

    if (c == "jnz"):
        if (getValue(cmd[1]) != 0):
            cur += getValue(cmd[2])
        else:
            cur += 1
    else:
        if(c == "set"):
            r[cmd[1]] = getValue(cmd[2])

        if(c == "sub"):
            r[cmd[1]] -= getValue(cmd[2])

        if (c == "mul"):
            r[cmd[1]] *= getValue(cmd[2])
            count += 1

        cur += 1

print("Part one: %d" % count)

h = 0

for x in range(107900, 124900 + 1, 17):
    for i in range(2, x):
        if x % i == 0:
            h += 1
            break

print("Part two: %d" % h)


class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def sumOfLeftLeaves(root: TreeNode):
    if not root:
        return 0
    res,lmost = 0,0
    # 从当前节点看有没有左叶子
    if root.left is not None and root.left.left is None and root.left.right is None:
        lmost = root.left.val
    lpart = sumOfLeftLeaves(root.left)
    rpart = sumOfLeftLeaves(root.right)
    res = lmost + lpart + rpart
    return res

if __name__ == "__main__":
    input = TreeNode(1, TreeNode(2), TreeNode(3, TreeNode(5)))
    output = sumOfLeftLeaves(input)
    print(output)


import sys
import threading
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QRadioButton,
    QLabel,
    QLineEdit,
    QTextEdit,
    QPushButton,
    QButtonGroup,
    QMessageBox,
    QAction
)
from PyQt5.QtGui import QPalette, QColor
from bs4 import BeautifulSoup
import time
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import WebDriverException
# Use to clean the code
from retailers_links import retailersFile
from browers_choice import initialize_driver
from theme_changer import read_theme_settings
from version_checker import check_for_updates, version_check

class MyApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        theme_mode, background_color, text_color, buttons_color, text_size, font_style = read_theme_settings("config.json")

        # Set background color for the entire application
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(background_color))
        self.setPalette(palette)

        # Apply text color to various elements
        self.product_label.setStyleSheet(f"color: {text_color};font-size: {text_size}; font-family: {font_style}")
        self.scraper_output_label.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        self.start_button.setStyleSheet(f"color: {text_color}; background-color: {buttons_color};")
        #self.stop_button.setStyleSheet(f"color: {text_color}; background-color: {buttons_color};")
        self.product_input.setStyleSheet(f"color: {text_color}; background-color: transparent; font-size: {text_size}; font-family: {font_style}")
        self.scraper_output.setStyleSheet(f"color: {text_color}; background-color: transparent; font-size: {text_size}; font-family: {font_style}")
        self.firefox_radio.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        self.chrome_radio.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        self.edge_radio.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        self.original_button_palette = self.start_button.palette()
    def init_ui(self):

        # MenuBar
        menubar = self.menuBar()

        # Create an 'Options' menu with 'Check for Updates', 'About' and 'Exit' actions
        options_menu = menubar.addMenu('Options')

        check_for_updates_action = QAction('Check for Updates', self)
        check_for_updates_action.triggered.connect(lambda:check_for_updates(self))

        # Add the action to the 'Options' menu
        options_menu.addAction(check_for_updates_action)

        about_action = QAction('About',self)
        about_action.triggered.connect(lambda:version_check(self))
        options_menu.addAction(about_action)

        # Create the "Refresh Theme" QAction
        refresh_action = QAction("Refresh Theme", self)
        refresh_action.triggered.connect(self.refresh_theme)
        options_menu.addAction(refresh_action)

        exit_action = QAction('Exit', self)
        exit_action.triggered.connect(self.close)
        options_menu.addAction(exit_action)

        self.central_widget = QWidget(self)
        self.setCentralWidget(self.central_widget)

        # Create the layout
        layout = QVBoxLayout()

        # Browser selection radio buttons
        browser_layout = QHBoxLayout()
        self.browser_group = QButtonGroup()

        self.firefox_radio = QRadioButton('Firefox')
        self.chrome_radio = QRadioButton('Chrome')
        self.edge_radio = QRadioButton('Edge')

        self.browser_group.addButton(self.firefox_radio, 1)
        self.browser_group.addButton(self.chrome_radio, 2)
        self.browser_group.addButton(self.edge_radio, 3)

        browser_layout.addWidget(self.firefox_radio)
        browser_layout.addWidget(self.chrome_radio)
        browser_layout.addWidget(self.edge_radio)

        layout.addLayout(browser_layout)

        # Product label and input field
        self.product_layout = QHBoxLayout()
        self.product_label = QLabel('Product:')
        self.product_input = QLineEdit()
        self.product_input.returnPressed.connect(self.start_scraper)# When enter is pressed start scraping.
        self.product_layout.addWidget(self.product_label)
        self.product_layout.addWidget(self.product_input)
        layout.addLayout(self.product_layout)

        # Scraper output label and text area
        self.scraper_output_layout = QHBoxLayout()
        self.scraper_output_label = QLabel('Scraper Output:')
        self.scraper_output = QTextEdit()
        self.scraper_output.setReadOnly(True)
        self.scraper_output_layout.addWidget(self.scraper_output_label)
        self.scraper_output_layout.addWidget(self.scraper_output)
        layout.addLayout(self.scraper_output_layout)

        # Start button
        self.start_button = QPushButton('Start')
        self.start_button.clicked.connect(self.start_scraper)
        layout.addWidget(self.start_button)

        # Stop button
        self.stop_button = QPushButton('Not Scraping')
        self.stop_button.clicked.connect(self.stop_scraper)
        layout.addWidget(self.stop_button)
        self.stop_button.setEnabled(False)
        self.stop_button.setStyleSheet("background-color: #CCCCCC; color: #808080;")

        # Apply layout to central widget
        self.central_widget.setLayout(layout)

        self.setWindowTitle('CoolyerScraper GUI')
        self.setGeometry(100, 100, 600, 400)

    def refresh_theme(self):
        theme_mode, background_color, text_color, buttons_color, text_size, font_style = read_theme_settings("config.json")
        # Apply text color to various elements
        self.product_label.setStyleSheet(f"color: {text_color};font-size: {text_size}; font-family: {font_style}")
        self.scraper_output_label.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        self.start_button.setStyleSheet(f"color: {text_color}; background-color: {buttons_color};")
        #self.stop_button.setStyleSheet(f"color: {text_color}; background-color: {buttons_color};")
        self.product_input.setStyleSheet(f"color: {text_color}; background-color: transparent; font-size: {text_size}; font-family: {font_style}")
        self.scraper_output.setStyleSheet(f"color: {text_color}; background-color: transparent; font-size: {text_size}; font-family: {font_style}")
        self.firefox_radio.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        self.chrome_radio.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        self.edge_radio.setStyleSheet(f"color: {text_color}; font-size: {text_size}; font-family: {font_style}")
        print("Themed Refreshed")

    def start_scraper(self, from_enter=False):
        theme_mode, background_color, text_color, buttons_color, text_size, font_style = read_theme_settings("config.json")
        # Get the selected browser choice
        browser_choice = self.browser_group.checkedId()
        if browser_choice == -1:
            self.scraper_output.clear()
            self.scraper_output.append("Please choose a browser")
            return  # Exit the function if no browser is selected

        product_name = self.product_input.text()

        if not product_name:
            self.scraper_output.clear()
            self.scraper_output.append("Please enter a product name")
            return  # Exit the function if the product name is empty
        # Disable the start button while scraping is in progress
        self.start_button.setEnabled(False)
        self.start_button.setText("Scraping")
        self.start_button.setStyleSheet("background-color: #CCCCCC; color: #808080;")
        # Stop button
        self.stop_button.setEnabled(True)
        self.stop_button.setText("Stop")
        self.stop_button.setStyleSheet(f"color: {text_color}; background-color: {buttons_color};")

        # Get the product name from the input field
        product_name = self.product_input.text()

        # Clear the scraper output
        self.scraper_output.clear()

        # Start the thread to allow it not to stop responding.
        self.scraper_thread = ScraperThread(browser_choice, product_name)

        # Set scraping flag to True
        self.scraper_thread.scraping = True

        # The code to allow it to scrape in real time
        self.scraper_thread.price_scraped.connect(self.update_scraper_output_with_price)

        # Calls what retailer its scraping
        self.scraper_thread.retailer_current.connect(self.retailercurrent)

        # Connect the scraping completion signal to the handling method
        self.scraper_thread.scraping_complete.connect(self.handle_scraping_complete)
        self.scraper_thread.start()

    # Checks if scraping is done.
    def handle_scraping_complete(self, complete):
        if complete:
            # Re-enable the start button after scraping is complete
            self.start_button.setEnabled(True)
            self.start_button.setText("Start Scraping")
            theme_mode, background_color, text_color, buttons_color, text_size, font_style = read_theme_settings("config.json")
            self.start_button.setStyleSheet(f"color: {text_color}; background-color: {buttons_color};")

    def retailercurrent(self, retailer):
        # Tells you real time what retailer is scraping from.
        self.scraper_output.append(f"Retailer:{retailer}\n")

    def update_scraper_output_with_price(self, price):
        # This method is called when a price is scraped
        self.scraper_output.append(f"{price}")

    def stop_scraper(self):
            # Set scraping flag to False and stops scraping
            self.scraper_thread.scraping = False
            self.scraper_output.append("User stopped scraping process")
            self.stop_button.setEnabled(False)
            self.stop_button.setStyleSheet("background-color: #CCCCCC; color: #808080;")

class ScraperThread(QObject, threading.Thread):
    scraping_complete = pyqtSignal(bool)
    price_scraped = pyqtSignal(str)
    retailer_current = pyqtSignal(str)

    def __init__(self, browser_choice, product_name):
        super().__init__()
        self.browser_choice = browser_choice
        self.product_name = product_name
        self.scraper_output = QTextEdit()
        self.scraping = True
    def run(self):
        try:
            # Call the initialize_driver function to create the WebDriver instance
            driver = initialize_driver(self.browser_choice)
            if driver:
                retailers = retailersFile()
                # Define an empty dictionary to store the product prices and names
                product_data = {}
                # Loop through each retailer and their search URL
                for retailer, url in retailers.items():
                    retailers = retailersFile()
                    webWaitTime = 4
                    # Construct the full search URL by appending the product name to the base URL
                    search_url = retailers[retailer]['url'] + self.product_name
                    # Open the search URL using the chosen browser
                    driver.get(search_url)
                    # Wait for some time for the web page to load
                    time.sleep(webWaitTime)
                    # Parse the HTML source of the web page using BeautifulSoup
                    soup = BeautifulSoup(driver.page_source, 'html.parser')
                    print(f"Scraping: {retailer}")
                    self.retailer_current.emit(retailer)
                    # Stop the scraper when stop is pressed
                    if self.scraping == False:
                        self.scraping_complete.emit(True)
                        print("Scrape complete")
                        driver.quit()
                    # Scrape the data for each retailer using different CSS selectors or XPath expressions
                    if retailer == 'Tesco':
                        try:
                            tiles = driver.find_elements(By.XPATH, '//li[@class="product-list--list-item"]')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ''
                            for index, tile in enumerate(tiles):
                                try:
                                    price_element = WebDriverWait(tile, webWaitTime).until(
                                        EC.visibility_of_element_located((By.CLASS_NAME, 'beans-price__text'))
                                    )

                                    item_name_element = WebDriverWait(tile, webWaitTime).until(
                                    EC.visibility_of_element_located((
                                                    By.XPATH,
                                                    './/span[@class= "styled__Text-sc-1i711qa-1 xZAYu ddsweb-link__text"]')))

                                    pricePerMil_element = WebDriverWait(tile, webWaitTime).until(
                                    EC.visibility_of_element_located((By.CSS_SELECTOR, '.styled__StyledFootnote-sc-119w3hf-7.icrlVF.styled__Subtext-sc-8qlq5b-2.bNJmdc.beans-price__subtext')))
                                    try:
                                        clubcard_price_element = tile.find_element(By.XPATH, './/span[contains(@class, "offer-text")]')
                                        clubcard_price = clubcard_price_element.text.strip()
                                    except NoSuchElementException:
                                        clubcard_price = None

                                    name_html = item_name_element.get_attribute('outerHTML')
                                    soup = BeautifulSoup(name_html, 'html.parser')
                                    name = soup.get_text(strip=True)

                                    # Extract the regular price from the price element
                                    price_html = price_element.get_attribute('innerHTML')
                                    soup = BeautifulSoup(price_html, 'html.parser')
                                    price = soup.get_text(strip=True)
                                    pricePerMil = pricePerMil_element.text.strip()
                                    if clubcard_price is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price} {pricePerMil}|{clubcard_price}\n")
                                    else:
                                        product_data[retailer] += (f"|Tile {retailer} {index + 1} - Name: {name}, Price: {price} {pricePerMil}|\n")

                                except Exception as e:
                                        print("Error parsing tile:", str(e))
                                    # Add an error handler or continue based on your needs

                        except Exception as e:
                            print(f"{retailer} error: {str(e)}")

                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'Asda':
                        try:
                            tiles = driver.find_elements(By.XPATH, '//li[@class=" co-item co-item--rest-in-shelf "]')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ""
                            for index, tile in enumerate(tiles):
                                try:
                                    price_element = WebDriverWait(tile, webWaitTime).until(
                                    EC.visibility_of_element_located((By.XPATH, './/strong[contains(@class,"co-product__price")]'))
                                    )

                                    item_name_element = tile.find_element(By.XPATH, './/h3[@class="co-product__title"]/a[@class="co-product__anchor"]')

                                    try:
                                        Asdalitres_element = tile.find_element(By.XPATH, './/span[@class="co-product__volume co-item__volume"]')
                                        Asdalitres = Asdalitres_element.text.strip()

                                    except NoSuchElementException:
                                        Asdalitres = None
                                    try:
                                        deal_element = tile.find_element(By.XPATH, '//a[@data-auto-id="linkPromoDetail"]')
                                        deal_text = deal_element.find_element(By.CLASS_NAME, 'link-save-banner-large__config--font-normal').text
                                        deal_price = deal_element.find_element(By.CLASS_NAME, 'link-save-banner-large__config--font-huge').text
                                        deal_info = f"{deal_text} {deal_price}"
                                    except NoSuchElementException:
                                        deal_info =""
                                    name = item_name_element.text.strip()
                                    price_html = price_element.get_attribute('innerHTML')
                                    soup = BeautifulSoup(price_html, 'html.parser')
                                    price = soup.get_text(strip=True).replace("now", "")

                                    if Asdalitres is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name} {Asdalitres}, Price: {price}|{deal_info}\n")
                                    else:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|{deal_info}\n")
                                except Exception as e:
                                        print(f"{retailer} error: {str(e)}")
                                    # Add an error handler or continue based on your needs

                        except Exception as e:
                            print(f"{retailer} error: {str(e)}")
                        # Code to allow it to update in real time.
                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'B&M':
                        try:
                            tiles = driver.find_elements(By.XPATH, '//li[@class="col-6 col-landscape-4 mt-3 pt-lg-3 px-lg-3"]')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ''
                            for index, tile in enumerate(tiles):
                                try:
                                    price_element = WebDriverWait(tile, webWaitTime).until(
                                        EC.visibility_of_element_located((By.XPATH, '//span[contains(@class, "d-block h5 mb-0 text-secondary bm-line-compact")][contains(@class, "bm-line-compact")]'))
                                    )

                                    item_name_element = WebDriverWait(tile, webWaitTime).until(
                                        EC.visibility_of_element_located((By.CLASS_NAME, 'bm-product-stretch-link')))

                                    try:
                                        bmdeals_element = tile.find_element(By.XPATH, './/span[contains(@class, "offer-text")]')
                                        bmdeals = bmdeals_element.text.strip()
                                    except NoSuchElementException:
                                        bmdeals = None
                                    try:
                                        bmoffer_element = tile.find_element(By.XPATH, './/span[contains(@class, "badge badge-primary text-wrap")]')
                                        bmoffer = bmoffer_element.text.strip()
                                    except NoSuchElementException:
                                        bmoffer =None

                                    name_html = item_name_element.get_attribute('outerHTML')
                                    soup = BeautifulSoup(name_html, 'html.parser')
                                    name = soup.get_text(strip=True)

                                    # Extract the regular price from the price element
                                    price_html = price_element.get_attribute('innerHTML')
                                    soup = BeautifulSoup(price_html, 'html.parser')
                                    price = soup.get_text(strip=True)
                                    if bmdeals is not None and bmoffer is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}| Deals: {bmdeals}, Offers: {bmoffer}|\n")
                                    elif bmdeals is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}| Deals: {bmdeals}|\n")
                                    elif bmoffer is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}| Offers: {bmoffer}|\n")
                                    else:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|\n")

                                except Exception as e:
                                    print(f"{retailer} error: {str(e)}")
                            # Add an error handler or continue based on your needs

                        except Exception as e:
                                print(f"{retailer} error: {str(e)}")
                        # Code to allow it to update in real time.
                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'Sainsburys':
                        try:
                            tiles1 = driver.find_elements(By.XPATH, '//li[@class= "pt-grid-item ln-o-grid__item ln-u-1/2@xs ln-u-1/3@sm ln-u-1/4@md ln-u-1/5@xl"]')[:retailers[retailer]['num_tiles_to_search']]
                            tiles2 = driver.find_elements(By.XPATH, '//li[@class="gridItem"]')[:retailers[retailer]['num_tiles_to_search']]
                            tiles = tiles1 + tiles2
                            product_data[retailer] = ""
                            for index, tile in enumerate(tiles):
                                try:
                                    price_element1 = tile.find_elements(By.XPATH, './/div[@class = "pricing"]/p[@class = "pricePerUnit"]')
                                    price_element2 = tile.find_elements(By.XPATH, './/span[contains(@class, "pt__cost__retail-price pt__cost__retail-price--with-nectar-not-associated")]')
                                    price_element3 = tile.find_elements(By.CLASS_NAME, 'pt__cost__retail-price')
                                    # Extract the price based on whichever element is found
                                    price = ""
                                    if price_element1:
                                        price = price_element1[0].text.strip()
                                    elif price_element2:
                                        price = price_element2[0].text.strip()
                                    elif price_element3:
                                        price = price_element3[0].text.strip()

                                    name_element1 = tile.find_elements(By.XPATH, './/a[contains(@class, "pt__link")]')
                                    name_element2 = tile.find_elements(By.XPATH, './/div[contains(@class, "productNameAndPromotions")]/h3/a')

                                    # Extract the product name based on whichever element is found
                                    name = ""
                                    if name_element1:
                                        name = name_element1[0].text.strip()
                                    elif name_element2:
                                        name = name_element2[0].text.strip()
                                    try:
                                        nectar_price_element1 = tile.find_elements(By.XPATH, './/div[@class = "pricing"]/p[@class = "pricePerUnit nectarPrice"]')
                                        nectar_price_element2 = tile.find_elements(By.XPATH, './/span[contains(@class, "pt__cost--price")]')
                                        # Extract the nectar price based on whichever element is found
                                        nectar_price = ""
                                        if nectar_price_element1:
                                            nectar_price = "Nectar price: " + nectar_price_element1[0].text.strip()
                                        elif nectar_price_element2:
                                            nectar_price = "Nectar price: " + nectar_price_element2[0].text.strip()
                                    except NoSuchElementException:
                                        nectar_price = ''

                                    # Extract the regular price from the price element

                                    product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|{nectar_price}\n")
                                except Exception as e:
                                    print(f"{retailer} error: {str(e)}")
                        except Exception as e:
                                print(f"{retailer} error: {str(e)}")
                        # Code to allow it to update in real time.
                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'Iceland':
                        try:
                            tiles = driver.find_elements(By.CLASS_NAME, 'grid-tile ')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ""
                            for index, tile in enumerate(tiles):
                                try:
                                    price_elements = tile.find_elements(By.XPATH, './/span[@class="product-sales-price"]/span')
                                    price = price_elements[0].text.strip()

                                    item_name_element = tile.find_element(By.CLASS_NAME, 'name-link')

                                    try:
                                        iceLandOffers_element = tile.find_element(By.CLASS_NAME, 'price')
                                        iceLandOffers = iceLandOffers_element.text.strip()

                                    except NoSuchElementException:
                                        iceLandOffers = None

                                    name = item_name_element.text.strip()

                                    if iceLandOffers is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|Multibuy Price: {iceLandOffers} each \n")
                                    else:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|\n")
                                except Exception as e:
                                        pass # There is a list index out of range error but shows all the correct prices and products so no idea.

                        except Exception as e:
                            print(f"{retailer} error: {str(e)}")
                        # Code to allow it to update in real time.
                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'Poundshop':
                        try:
                            tiles = driver.find_elements(By.XPATH, '//li[@class= "rrp item product product-item"]')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ""
                            for index, tile in enumerate(tiles):
                                try:
                                    price_element = WebDriverWait(driver, webWaitTime).until(
                                    EC.visibility_of_element_located((By.CLASS_NAME, 'price'))
                                    )

                                    item_name_element = tile.find_element(By.CLASS_NAME, 'product-item-link')

                                    try:
                                        poundShopOffersStart_element = tile.find_element(By.CLASS_NAME, 'price')
                                        poundShopOffersStart = poundShopOffersStart_element.text.strip()
                                        poundShopOffersExtra_element = tile.find_element(By.CLASS_NAME, 'qty-label')
                                        poundShopOffersExtra = poundShopOffersExtra_element.text.strip()
                                        poundShopOffers = (f" {poundShopOffersStart} {poundShopOffersExtra}")
                                    except NoSuchElementException:
                                        poundShopOffers = None

                                    name = item_name_element.text.strip()
                                    price_html = price_element.get_attribute('innerHTML')
                                    soup = BeautifulSoup(price_html, 'html.parser')
                                    price = soup.get_text(strip=True).replace("now", "")

                                    if poundShopOffers is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: £{price}|{poundShopOffers}\n")
                                    else:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: £{price}|\n")
                                except Exception as e:
                                        print(f"{retailer} error: {str(e)}")
                                    # Add an error handler or continue based on your needs

                        except Exception as e:
                            print(f"{retailer} error: {str(e)}")
                        # Code to allow it to update in real time.
                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'Poundland':
                        try:
                            tiles = driver.find_elements(By.XPATH, '//li[@class= " item product product-item c-product c-product__item"]')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ""
                            for index, tile in enumerate(tiles):
                                try:
                                    price_element = tile.find_elements(By.CLASS_NAME, 'c-product__price')
                                    price = price_element[0].text.strip()
                                    item_name_element = tile.find_element(By.CLASS_NAME, 'c-product__title')

                                    try:
                                        poundLandOffers_element = tile.find_element(By.CLASS_NAME, 'c-product__promo')
                                        poundLandOffers = poundLandOffers_element.text.strip()

                                    except NoSuchElementException:
                                        poundLandOffers = None

                                    name = item_name_element.text.strip()

                                    if poundLandOffers is not None:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|{poundLandOffers} each \n")
                                    else:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|\n")
                                except Exception as e:
                                        print(f"{retailer} error: {str(e)}")
                                    # Add an error handler or continue based on your needs

                        except Exception as e:
                            print(f"{retailer} error: {str(e)}")
                        # Code to allow it to update in real time.
                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'Aldi':
                        try:
                            tiles = driver.find_elements(By.XPATH, '//div[@data-qa="search-result"]')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ""
                            for index, tile in enumerate(tiles):
                                    try:
                                        name_element = tile.find_element(By.XPATH, './/a[@data-qa="search-product-title"]')
                                        price_element = tile.find_element(By.XPATH, './/span[@class="h4"]/span')

                                        # Extract the product name and price
                                        name = name_element.text.strip()
                                        price = price_element.text.strip()

                                        # Add the extracted data to product_data
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name}, Price: {price}|\n")
                                    except Exception as e:
                                        print(f"{retailer} error: {str(e)}")
                        except Exception as e:
                            print(f"{retailer} error: {str(e)}")
                        # Code to allow it to update in real time.
                        self.price_scraped.emit(product_data[retailer])
                    elif retailer == 'Morrisons':
                        try:
                            tiles = driver.find_elements(By.XPATH, '//li[contains(@class, "fops-item") and contains(@class, "fops-item--on_offer") or contains (@class, "fops-item fops-item--cluster")]')[:retailers[retailer]['num_tiles_to_search']]
                            product_data[retailer] = ""
                            for index, tile in enumerate(tiles):
                                try:
                                    name_element = tile.find_element(By.XPATH, './/div[@class ="fop-description"]/h4[@class="fop-title"]')
                                    price_element = tile.find_elements(By.XPATH, './/div[@class = "price-group-wrapper"]/span[@class="fop-price"]')
                                    price_element1 = tile.find_elements(By.XPATH, './/div[@class = "price-group-wrapper"]/span[@class ="fop-price price-offer"]')
                                    price = ""
                                    if price_element:
                                        price = price_element[0].text.strip()

                                    elif price_element1:
                                        price = price_element1[0].text.strip()
                                    # Extract the product name and price
                                    name = name_element.text.strip()
                                    try:
                                    # Check if there's a promotional offer
                                        promo_element = tile.find_element(By.XPATH, './/a[@class="fop-row-promo promotion-offer"]/span')
                                        promo = promo_element.text.strip()
                                    except NoSuchElementException:
                                        promo = None
                                    try:
                                        weight_element = tile.find_element(By.XPATH, './/span[@class="fop-catch-weight"]')
                                        weight = weight_element.text.strip()
                                    except NoSuchElementException:
                                        weight = None
                                    # Add the extracted data to product_data
                                    if promo:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name} {weight}, Price: {price}|{promo}\n")
                                    else:
                                        product_data[retailer] += (f"|Tile {index + 1} - Name: {name} {weight}, Price: {price}|\n")
                                except Exception as e:
                                    print(f"{retailer} error: {str(e)}")
                            self.price_scraped.emit(product_data[retailer])
                        except Exception as e:
                            print(f"{retailer} error: {str(e)}")

        except WebDriverException as e:
                print(f"WebDriverException: {str(e)}")
        except Exception as e:
            print(f"An error occurred: {str(e)}")
        finally:
            # Set scraping flag to False when scraping is done
            self.scraping = False
            self.scraping_complete.emit(True)
            if driver:
                print("Scrape complete")
                driver.quit()

def main():
    app = QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()


import logging

import requests
from concepts.models import Item
from django.db.utils import IntegrityError
from slurper.wd_raw_item import WD_OTHER_SOURCES, BaseWdRawItem

class WikidataSlurper:
    SPARQL_URL = "https://query.wikidata.org/sparql"

    SPARQL_QUERY_OPTIONS = """
  OPTIONAL
  { ?item wdt:P18 ?image . }
  OPTIONAL
  {
    ?wp_en rdf:type schema:Article;
      schema:isPartOf <https://en.wikipedia.org/>;
      schema:about ?item .
  }
  # except for natural numbers
  MINUS {
    ?item wdt:P31 wd:Q21199 .
  }
  # except for humans
  FILTER NOT EXISTS{ ?item wdt:P31 wd:Q5 . }
  # collect the label and description
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
"""

    def __init__(self, source, query, limit=None):
        self.source = source
        self.query = (
            """
SELECT
  DISTINCT ?item ?itemLabel ?itemDescription ?image ?wp_en
 """
            + self._sparql_source_vars_select()
            + """
WHERE {
"""
            + query
            + self._sparql_source_vars_triples()
            + self.SPARQL_QUERY_OPTIONS
            + (f"LIMIT {limit}" if limit is not None else "")
        )
        self.raw_data = self.fetch_json()

    def _sparql_source_vars_select(self):
        def to_var(source_dict):
            return " ?" + source_dict["json_key"]

        return " ".join(map(to_var, WD_OTHER_SOURCES.values()))

    def _sparql_source_vars_triples(self):
        def to_triple(source_dict):
            source_var_triple = "  OPTIONAL\n  { ?item "
            source_var_triple += source_dict["wd_property"]
            source_var_triple += " ?" + source_dict["json_key"]
            source_var_triple += " . }"
            return source_var_triple

        return "\n".join(map(to_triple, WD_OTHER_SOURCES.values()))

    def fetch_json(self):
        response = requests.get(
            self.SPARQL_URL,
            params={"format": "json", "query": self.query},
        )
        return response.json()["results"]["bindings"]

    def get_items(self):
        for json_item in self.raw_data:
            raw_item = BaseWdRawItem.raw_item(self.source, json_item)
            yield raw_item.to_item()
            if self.source != Item.Source.WIKIDATA:
                raw_item_wd = raw_item.switch_source_to(Item.Source.WIKIDATA)
                if not raw_item_wd.item_exists():
                    yield raw_item_wd.to_item()
            if raw_item.has_source(Item.Source.WIKIPEDIA_EN):
                raw_item_wp_en = raw_item.switch_source_to(Item.Source.WIKIPEDIA_EN)
                if not raw_item_wp_en.item_exists():
                    yield raw_item_wp_en.to_item()

    def save_items(self):
        for item in self.get_items():
            try:
                item.save()
            except IntegrityError:
                logging.log(
                    logging.INFO,
                    f"Item {item.source} {item.identifier} is already in the database.",
                )

    def save_links(self):
        for json_item in self.raw_data:
            BaseWdRawItem.raw_item(self.source, json_item).save_links()

SLURPERS = [
    WikidataSlurper(Item.Source.WIKIDATA, query)
    for query in [
        """
  # anything part of a topic that is studied by mathmatics
  ?item wdt:P31 ?topic .
  ?topic wdt:P2579 wd:Q395 .
""",
        """
  # concepts studied by an area of mathematics
  ?item wdt:P2579 ?area .
  ?area wdt:P31 wd:Q1936384 .
""",
        """
  # concepts of areas of mathematics
  ?item p:P31 ?of .
  ?of ps:P31 wd:Q151885 .
  ?of pq:P642/p:P31/ps:P31 wd:Q1936384 .
""",
    ]
]

SLURPERS += [
    WikidataSlurper(
        source,
        f"\n  ?item {property['wd_property']} ?{property['json_key']} .\n",
    )
    for source, property in WD_OTHER_SOURCES.items()
]