from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import datetime

from googlecloudsdk.api_lib.storage import cloud_api
from googlecloudsdk.api_lib.storage import errors as api_errors
from googlecloudsdk.command_lib.storage import errors as command_errors
from googlecloudsdk.command_lib.storage import manifest_util
from googlecloudsdk.command_lib.storage import storage_url
from googlecloudsdk.command_lib.storage.resources import resource_util
from googlecloudsdk.command_lib.storage.tasks import task
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import log
from googlecloudsdk.core import properties

_EARLY_DELETION_MINIMUM_DAYS = {
    'nearline': 30,
    'coldline': 90,
    'archive': 365,
}

class CopyTask(task.Task):
  """Parent task that handles common attributes and an __init__ status print."""

  def __init__(
      self,
      source_resource,
      destination_resource,
      posix_to_set=None,
      print_source_version=False,
      user_request_args=None,
      verbose=False,
  ):
    """Initializes task.

    Args:
      source_resource (resource_reference.Resource): Source resource to copy.
      destination_resource (resource_reference.Resource): Target resource to
        copy to.
      posix_to_set (PosixAttributes|None): POSIX info set as custom cloud
        metadata on target.
      print_source_version (bool): Print source object version in status message
        enabled by the `verbose` kwarg.
      user_request_args (UserRequestArgs|None): Various user-set values
        typically converted to an API-specific RequestConfig.
      verbose (bool): Print a "copying" status message on initialization.
    """
    super(CopyTask, self).__init__()
    self._source_resource = source_resource
    self._destination_resource = destination_resource
    self._posix_to_set = posix_to_set
    self._print_source_version = print_source_version
    self._user_request_args = user_request_args
    self._verbose = verbose

    self._send_manifest_messages = bool(
        self._user_request_args and self._user_request_args.manifest_path
    )

    if verbose:
      if print_source_version:
        source_string = source_resource.storage_url.url_string
      else:
        source_string = source_resource.storage_url.versionless_url_string
      log.status.Print(
          'Copying {} to {}'.format(
              source_string,
              destination_resource.storage_url.versionless_url_string,
          )
      )

class CopyTaskWithExitHandler(CopyTask):
  """Parent task that overrides exit handler for copy tasks."""

  def exit_handler(self, error=None, task_status_queue=None):
    """Send copy result info to manifest if requested."""
    if error and self._send_manifest_messages:
      if not task_status_queue:
        raise command_errors.Error(
            'Unable to send message to manifest for source: {}'.format(
                self._source_resource
            )
        )
      manifest_util.send_error_message(task_status_queue, self._source_resource,
                                       self._destination_resource, error)

def get_no_clobber_message(destination_url):
  """Returns standardized no clobber warning."""
  return 'Skipping existing destination item (no-clobber): {}'.format(
      destination_url)

def check_for_cloud_clobber(user_request_args, api_client,
                            destination_resource):
  """Returns if cloud destination object exists if no-clobber enabled."""
  if not (user_request_args and user_request_args.no_clobber):
    return False
  try:
    api_client.get_object_metadata(
        destination_resource.storage_url.bucket_name,
        destination_resource.storage_url.object_name,
        fields_scope=cloud_api.FieldsScope.SHORT)
  except api_errors.NotFoundError:
    return False
  return True

def get_generation_match_value(request_config):
  """Prioritizes user-input generation over no-clobber zero value."""
  if request_config.precondition_generation_match is not None:
    return request_config.precondition_generation_match
  if request_config.no_clobber:
    return 0
  return None

def raise_if_mv_early_deletion_fee_applies(object_resource):
  """Raises error if Google Cloud Storage object will incur an extra charge."""
  if not (properties.VALUES.storage.check_mv_early_deletion_fee.GetBool() and
          object_resource.storage_url.scheme is storage_url.ProviderPrefix.GCS
          and object_resource.creation_time and
          object_resource.storage_class in _EARLY_DELETION_MINIMUM_DAYS):
    return

  minimum_lifetime = _EARLY_DELETION_MINIMUM_DAYS[
      object_resource.storage_class.lower()]
  creation_datetime_utc = resource_util.convert_datetime_object_to_utc(
      object_resource.creation_time)
  current_datetime_utc = resource_util.convert_datetime_object_to_utc(
      datetime.datetime.now())

  if current_datetime_utc < creation_datetime_utc + datetime.timedelta(
      days=minimum_lifetime):
    raise exceptions.Error(
        ('Deleting {} may incur an early deletion charge. Note: the source'
         ' object of a mv operation is deleted.\nThe object appears to have'
         ' been created on {}, and the minimum time before deletion for the {}'
         ' storage class is {} days.\nTo allow deleting the object anyways, run'
         ' "gcloud config set storage/check_mv_early_deletion_fee False"'
        ).format(object_resource, object_resource.creation_time,
                 object_resource.storage_class, minimum_lifetime))


import os
import pickle
import re

import torch
import nni.retiarii.nn.pytorch as nn
from nni.retiarii.nn.pytorch import LayerChoice
from nni.retiarii.serializer import model_wrapper

from blocks import ShuffleNetBlock, ShuffleXceptionBlock

@model_wrapper
class ShuffleNetV2OneShot(nn.Module):
    block_keys = [
        'shufflenet_3x3',
        'shufflenet_5x5',
        'shufflenet_7x7',
        'xception_3x3',
    ]

    def __init__(self, input_size=224, first_conv_channels=16, last_conv_channels=1024,
                 n_classes=1000, affine=False):
        super().__init__()

        assert input_size % 32 == 0
        self.stage_blocks = [4, 4, 8, 4]
        self.stage_channels = [64, 160, 320, 640]
        self._input_size = input_size
        self._feature_map_size = input_size
        self._first_conv_channels = first_conv_channels
        self._last_conv_channels = last_conv_channels
        self._n_classes = n_classes
        self._affine = affine
        self._layerchoice_count = 0

        # building first layer
        self.first_conv = nn.Sequential(
            nn.Conv2d(3, first_conv_channels, 3, 2, 1, bias=False),
            nn.BatchNorm2d(first_conv_channels, affine=affine),
            nn.ReLU(inplace=True),
        )
        self._feature_map_size //= 2

        p_channels = first_conv_channels
        features = []
        for num_blocks, channels in zip(self.stage_blocks, self.stage_channels):
            features.extend(self._make_blocks(num_blocks, p_channels, channels))
            p_channels = channels
        self.features = nn.Sequential(*features)

        self.conv_last = nn.Sequential(
            nn.Conv2d(p_channels, last_conv_channels, 1, 1, 0, bias=False),
            nn.BatchNorm2d(last_conv_channels, affine=affine),
            nn.ReLU(inplace=True),
        )
        self.globalpool = nn.AvgPool2d(self._feature_map_size)
        self.dropout = nn.Dropout(0.1)
        self.classifier = nn.Sequential(
            nn.Linear(last_conv_channels, n_classes, bias=False),
        )

        self._initialize_weights()

    def _make_blocks(self, blocks, in_channels, channels):
        result = []
        for i in range(blocks):
            stride = 2 if i == 0 else 1
            inp = in_channels if i == 0 else channels
            oup = channels

            base_mid_channels = channels // 2
            mid_channels = int(base_mid_channels)  # prepare for scale
            self._layerchoice_count += 1
            choice_block = LayerChoice([
                ShuffleNetBlock(inp, oup, mid_channels=mid_channels, ksize=3, stride=stride, affine=self._affine),
                ShuffleNetBlock(inp, oup, mid_channels=mid_channels, ksize=5, stride=stride, affine=self._affine),
                ShuffleNetBlock(inp, oup, mid_channels=mid_channels, ksize=7, stride=stride, affine=self._affine),
                ShuffleXceptionBlock(inp, oup, mid_channels=mid_channels, stride=stride, affine=self._affine)
            ], label="LayerChoice" + str(self._layerchoice_count))
            result.append(choice_block)

            if stride == 2:
                self._feature_map_size //= 2
        return result

    def forward(self, x):
        bs = x.size(0)
        x = self.first_conv(x)
        x = self.features(x)
        x = self.conv_last(x)
        x = self.globalpool(x)

        x = self.dropout(x)
        x = x.contiguous().view(bs, -1)
        x = self.classifier(x)
        return x

    def _initialize_weights(self):
        for name, m in self.named_modules():
            if isinstance(m, nn.Conv2d):
                if 'first' in name:
                    torch.nn.init.normal_(m.weight, 0, 0.01)
                else:
                    torch.nn.init.normal_(m.weight, 0, 1.0 / m.weight.shape[1])
                if m.bias is not None:
                    torch.nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                if m.weight is not None:
                    torch.nn.init.constant_(m.weight, 1)
                if m.bias is not None:
                    torch.nn.init.constant_(m.bias, 0.0001)
                torch.nn.init.constant_(m.running_mean, 0)
            elif isinstance(m, nn.BatchNorm1d):
                torch.nn.init.constant_(m.weight, 1)
                if m.bias is not None:
                    torch.nn.init.constant_(m.bias, 0.0001)
                torch.nn.init.constant_(m.running_mean, 0)
            elif isinstance(m, nn.Linear):
                torch.nn.init.normal_(m.weight, 0, 0.01)
                if m.bias is not None:
                    torch.nn.init.constant_(m.bias, 0)

def load_and_parse_state_dict(filepath="./data/checkpoint-150000.pth.tar"):
    checkpoint = torch.load(filepath, map_location=torch.device("cpu"))
    if "state_dict" in checkpoint:
        checkpoint = checkpoint["state_dict"]
    result = dict()
    for k, v in checkpoint.items():
        if k.startswith("module."):
            k = k[len("module."):]
        result[k] = v
    return result


import io
import math
from pathlib import Path, PureWindowsPath
from struct import unpack

class PDB:
	def __init__(self, path):
		with open(path,"rb") as fh:
			pdb_data = fh.read()
		self.fp = io.BytesIO(pdb_data)

		self.magic = self.fp.read(32)
		self.block_size = unpack("<I",self.fp.read(4))[0]
		self.free_block_map = self.fp.read(4)
		self.num_blocks = unpack("<I",self.fp.read(4))[0]
		self.num_dir_bytes = unpack("<I",self.fp.read(4))[0]
		self.reserved = self.fp.read(4)
		self.block_map_addr = unpack("<I",self.fp.read(4))[0]

		self.streams = []
		self.section_offsets = []

		self.omap_from_src = {}
		self.omap_to_src = {}

		self._get_stream_info()

	def _get_stream_info(self):
		# Seek to the address of the stream index
		stream_index_addr = self.block_size * self.block_map_addr
		self.fp.seek(stream_index_addr)

		stream_directory = b""
		byte_count = 0

		# Compile data from each block referenced in the stream index into a single byte stream
		for i in range(0, math.ceil(self.num_dir_bytes / self.block_size)):
			offset = unpack("<I",self.fp.read(4))[0] * self.block_size
			pos = self.fp.tell()
			self.fp.seek(offset)
			to_read = self.block_size
			if byte_count + to_read > self.num_dir_bytes:
				to_read = self.num_dir_bytes - byte_count
			stream_directory += self.fp.read(to_read)
			byte_count += to_read
			self.fp.seek(pos)

		sp = io.BytesIO(stream_directory)
		num_streams = unpack("<I",sp.read(4))[0]

		# Get the size and block count for each stream
		for i in range(num_streams):
			sp.seek((i+1) * 4)
			curr_stream = {}
			curr_stream['size'] = unpack("<I",sp.read(4))[0]
			curr_stream['block_count'] = math.ceil(curr_stream['size'] / self.block_size)
			curr_stream['blocks'] = []
			self.streams.append(curr_stream)

		# Populate the blocks array for each stream
		for i in self.streams:
			if i['size'] == 0:
				continue
			for block in range(i['block_count']):
				i['blocks'].append(unpack("<I",sp.read(4))[0])

	def read_stream(self, index):
		stream_data = b""
		byte_count = 0
		for i in self.streams[index]['blocks']:
			offset = i * self.block_size
			self.fp.seek(offset)
			to_read = self.block_size
			if to_read + byte_count > self.streams[index]['size']:
				to_read = self.streams[index]['size'] - byte_count
			stream_data += self.fp.read(to_read)
			byte_count += to_read

		return stream_data

	def parse_dbi(self):
		self.DBI_Stream = DBIStream(self.read_stream(3))
		try:
			from_src, to_src = self.DBI_Stream.parse_omap()
			from_src_data = self.read_stream(from_src)
			fh = io.BytesIO(from_src_data)
			for i in range(0, int(len(from_src_data) / 8)):
				mapping = fh.read(8)
				src = mapping[:4]
				to = mapping[4:]
				src = int.from_bytes(src, "little")
				to = int.from_bytes(to, "little")
				self.omap_from_src[src] = to

			to_src_data = self.read_stream(to_src)
			fh = io.BytesIO(to_src_data)
			for i in range(0, int(len(to_src_data) / 8)):
				mapping = fh.read(8)
				to = mapping[:4]
				src = mapping[4:]
				src = int.from_bytes(src, "little")
				to = int.from_bytes(to, "little")
				self.omap_to_src[to] = src
		except:
			# PDB does not contain the OMAP structures, should probably add code to do things here
			pass

		self._parse_section_headers()
		self.func_list = self._parse_func_symbols()

	def _parse_section_headers(self):
		section_header_data = self.read_stream(self.DBI_Stream.section_header_index)
		sp = io.BytesIO(section_header_data)
		count = 1
		while True:
			name = sp.read(8)
			if name == b"":
				break
			name = name.split(b"\x00")[0].decode("utf-8")
			virt_size = unpack("<I", sp.read(4))[0]
			virt_address = unpack("<I", sp.read(4))[0]
			raw_data = unpack("<I", sp.read(4))[0]
			raw_data_p = unpack("<I", sp.read(4))[0]
			self.section_offsets.append(virt_address)
			sp.seek(sp.tell()+16)

	def _parse_func_symbols(self):
		self.functions_by_offset = {}
		sym_stream = self.read_stream(self.DBI_Stream.ss_index)
		sp = io.BytesIO(sym_stream)
		while True:
			sym_len = sp.read(2)
			if sym_len == b'':
				break
			sym_len = unpack("<H", sym_len)[0]
			symbol = io.BytesIO(sp.read(sym_len))
			new_symbol = {}
			sym_type = unpack("<H", symbol.read(2))[0]
			if sym_type != 4366:
				continue
			flags = unpack("<I", symbol.read(4))[0]
			if flags != 2:
				continue
			offset = unpack("<I", symbol.read(4))[0]
			section = unpack("<H", symbol.read(2))[0]
			name = symbol.read(sym_len - 12).split(b"\x00")[0].decode("utf-8")
			self.functions_by_offset[offset + self.section_offsets[section - 1]] = name

class DBIStream:
	def __init__(self, stream_data):
		self.stream = io.BytesIO(stream_data)
		self.version_sig = self.stream.read(4)
		self.version_header = unpack("<I",self.stream.read(4))[0]
		self.age = unpack("<I",self.stream.read(4))[0]
		self.gs_index = unpack("<H",self.stream.read(2))[0]
		self.build_number = unpack("<H",self.stream.read(2))[0]
		self.ps_index = unpack("<H",self.stream.read(2))[0]
		self.pdb_dll_version = unpack("<H",self.stream.read(2))[0]
		self.ss_index = unpack("<H",self.stream.read(2))[0]
		self.pdb_dll_rbld = unpack("<H",self.stream.read(2))[0]
		self.mod_info_size = unpack("<i",self.stream.read(4))[0]
		self.sec_cont_size = unpack("<i",self.stream.read(4))[0]
		self.sec_map_size = unpack("<i",self.stream.read(4))[0]
		self.src_info_size = unpack("<i",self.stream.read(4))[0]
		self.type_srv_map_size = unpack("<i",self.stream.read(4))[0]
		self.MFC_type_srv_index = unpack("<I",self.stream.read(4))[0]
		self.opt_dbg_header_size = unpack("<i",self.stream.read(4))[0]
		self.ec_sub_size = unpack("<i",self.stream.read(4))[0]
		self.flags = unpack("<H",self.stream.read(2))[0]
		self.machine = unpack("<H",self.stream.read(2))[0]
		self.padding = unpack("<I",self.stream.read(4))[0]

		self.omap_from_src = {}
		self.omap_to_src = {}

	def parse_omap(self):
		omap_to_src_offset = 64 + self.mod_info_size + self.sec_cont_size + self.sec_map_size + self.src_info_size + self.type_srv_map_size + self.ec_sub_size + 6
		self.stream.seek(omap_to_src_offset)
		omap_to_src_index = unpack("<H",self.stream.read(2))[0]
		omap_from_src_index = unpack("<H",self.stream.read(2))[0]
		self.section_header_index = unpack("<H",self.stream.read(2))[0]
		return (omap_from_src_index, omap_to_src_index)


import json

import requests
if __name__ == '__main__':
    post_url='https://fanyi.baidu.com/sug'
    word=input('key:')

    data={'kw':word}
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'
    }
    #确认响应数据是json时调用.json方法
    response=requests.post(url=post_url,data=data,headers=headers).json()
    print(response)
    fp=open('./'+word+'.json','w',encoding='utf-8')
    json.dump(response,fp=fp)


from gettext import translation
import random
from django.shortcuts import render, redirect
from django.db import connection
from django.contrib.auth.hashers import make_password
from django.http import HttpResponse, HttpResponseRedirect
from datetime import datetime, timedelta
from django.http import Http404, HttpResponseRedirect, HttpResponseForbidden, HttpResponseNotFound, HttpResponseBadRequest
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
import json
from django.http import JsonResponse
from django.db import IntegrityError
from datetime import datetime
from django.contrib import messages
import time
import openai
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Access the API key
api_key = os.getenv('MY_API_KEY')

cur_user = {}

def home(request):
    handle_comment_submission(request)

    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    is_seller = request.session.get('is_seller', '')

    context = {
        'user_type': user_type,
        'user_name': user_name,
        'is_seller': is_seller,
    }
    return render(request, 'home.html', context)

def handle_comment_submission(request):
    if request.method == 'POST' and 'comment' in request.POST:
        print('here1')
        if 'user_id' in request.session:
            user_id = request.session.get('user_id')
            report_content = request.POST.get('comment')
            print('here2')
            print(user_id)
            print(report_content)
            try:
                with connection.cursor() as cursor:
                    query = """
                        INSERT INTO VIOLATION_REPORTS (user_id, report_content)
                        VALUES (%s, %s);
                    """
                    cursor.execute(query, (user_id, report_content))
                    connection.commit()
                messages.success(
                    request, 'Your comment has been successfully submitted.')
            except Exception as e:
                print(f"An error occurred: {e}")
                messages.error(
                    request, 'An error occurred while submitting your comment.')

            return redirect('home')

def is_email_valid(email):
    with connection.cursor() as cursor:
        cursor.execute("SELECT email FROM USERS WHERE email = %s", [email])
        return cursor.fetchone() is not None

def register(request):
    message = None

    if request.method == "POST":
        user_type = request.POST.get('user_type', '')
        user_name = request.POST.get('user_name', '')
        email = request.POST.get('email', '')

        if not is_email_valid(email):  # Check if the email is not already in use
            try:
                with connection.cursor() as cursor:
                    query = """
                        INSERT INTO USERS (user_type, user_name, email)
                        VALUES (%s, %s, %s);
                    """
                    cursor.execute(query, (user_type, user_name, email))
                    connection.commit()

                message = "Registration successful! "
            except Exception as e:
                print(f"An error occurred: {e}")
                message = "An error occurred during registration. Please try again."
        else:
            message = "Email already in use. Please choose a different email."

    return render(request, 'register.html', {'message': message, 'current_page': 'register'})

def update_session(request, email):
    """
    Update the session with the user's data based on the provided email.
    """
    try:
        with connection.cursor() as cursor:
            query = """
                SELECT *
                FROM USERS
                WHERE email = %s;
            """
            cursor.execute(query, [email])
            user_data = cursor.fetchone()

            if user_data:
                # Extracting user data and updating the session
                request.session['user_id'] = user_data[0]
                request.session['user_type'] = user_data[1]
                request.session['user_name'] = user_data[2]
                request.session['email'] = user_data[3]
                request.session['balance'] = str(
                    user_data[4])  # Convert Decimal to string
                request.session['seller_rating'] = str(
                    user_data[5])  # Convert Decimal to string
                request.session['buyer_rating'] = str(
                    user_data[6])  # Convert Decimal to string
                request.session['num_of_seller_rating'] = user_data[7]
                request.session['num_of_buyer_rating'] = user_data[8]
                request.session['is_allow_chat'] = user_data[9]
                request.session['is_allow_list'] = user_data[10]
                is_seller(request, user_data[0])
            else:
                # Handle case where user data is not found
                # You can redirect to an error page or set an error message
                pass

    except Exception as e:
        print(f"An error occurred: {e}")
        # Optionally, handle the exception (e.g., set an error message, redirect)

def is_seller(request, user_id):
    is_seller = False
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT COUNT(*) FROM LISTED_VEHICLES
            WHERE seller_id = %s
        """, [user_id])
        count = cursor.fetchone()[0]
        if count > 0:
            is_seller = True

    request.session['is_seller'] = is_seller

def login(request):
    error_message = None  # Variable to hold the error message

    if request.method == "POST":
        user_email = request.POST.get('email', '')

        if user_email:
            try:
                if is_email_valid(user_email):
                    update_session(request, user_email)
                    return redirect('search_car')
                else:
                    error_message = "Email does not exist. Please try again or"
            except Exception as e:
                print(f"An error occurred: {e}")
                error_message = "An error occurred during login. Please try again."
        else:
            error_message = "Please enter an email address."

    return render(request, 'login.html', {'error_message': error_message, 'current_page': 'login'})

def is_email_valid(email):
    with connection.cursor() as cursor:
        cursor.execute("SELECT email FROM USERS WHERE email = %s", [email])
        return cursor.fetchone() is not None

def logout(request):
    request.session.flush()
    return redirect('home')

def profile(request):
    # enable write commnets
    handle_comment_submission(request)

    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    # Fetching data from the session
    user_id = request.session.get('user_id', '')
    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    email = request.session.get('email', '')
    balance = request.session.get('balance', '')
    seller_rating = request.session.get('seller_rating', '')
    buyer_rating = request.session.get('buyer_rating', '')
    num_of_seller_rating = request.session.get('num_of_seller_rating', '')
    num_of_buyer_rating = request.session.get('num_of_buyer_rating', '')
    is_allow_chat = request.session.get('is_allow_chat', '')
    is_allow_list = request.session.get('is_allow_list', '')
    is_seller = request.session.get('is_seller', '')

    listings = []
    if user_id:
        try:
            with connection.cursor() as cursor:
                query = """
                    SELECT lv.listing_id, lv.make, lv.model, lv.year_of_production,
                        lv.image_url, lv.listing_status, lv.listing_start_date,
                        vo.order_id, vo.is_paid, vo.is_shipped, lv.is_verified
                    FROM LISTED_VEHICLES lv
                    LEFT JOIN VEHICLE_ORDERS vo ON lv.listing_id = vo.listing_id
                    WHERE lv.seller_id = %s;
                """
                cursor.execute(query, [user_id])
                for row in cursor.fetchall():
                    # Check if order is paid and shipped
                    rate_buyer_link = None
                    if row[7] is not None and row[8] == b'\x01' and row[9] == b'\x01':
                        rate_buyer_link = f"seller_rate_buyer/{row[7]}/"
                    listings.append(row + (rate_buyer_link,))

        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error
    print(listings)

    # Fetch the user's biddings from the database
    biddings = []

    if user_id:
        try:
            with connection.cursor() as cursor:
                query = """
                    SELECT b.bidding_id, b.listing_id, v.make, v.model, v.year_of_production,
                        v.image_url, v.listing_status, b.bidding_amount, b.bidding_date, b.is_winner,
                        s.user_id, s.user_name
                    FROM BIDDINGS b
                    INNER JOIN LISTED_VEHICLES v ON b.listing_id = v.listing_id
                    INNER JOIN USERS s ON v.seller_id = s.user_id
                    WHERE b.user_id = %s;
                """
                cursor.execute(query, [user_id])
                biddings = cursor.fetchall()
        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error

    # Creating the context dictionary
    context = {
        'user_id': user_id,
        'user_type': user_type,
        'user_name': user_name,
        'email': email,
        'balance': balance,
        'seller_rating': seller_rating,
        'buyer_rating': buyer_rating,
        'num_of_seller_rating': num_of_seller_rating,
        'num_of_buyer_rating': num_of_buyer_rating,
        'is_allow_chat': is_allow_chat,
        'is_allow_list': is_allow_list,
        'listings': listings,
        'biddings': biddings,
        'is_seller': is_seller,
        'current_page': 'profile',
    }

    return render(request, 'profile.html', context)

def report(request):
    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM VIOLATION_REPORTS")
        violation_reports = cursor.fetchall()

    if request.method == 'POST':
        report_id_to_delete = request.POST.get('report_id_to_delete')
        if report_id_to_delete:
            # Delete the report with the specified report_id using a cursor
            with connection.cursor() as cursor:
                cursor.execute("DELETE FROM VIOLATION_REPORTS WHERE report_id = %s", [
                               report_id_to_delete])

            # Redirect back to the report page after deleting
            return redirect('report')

    # Add violation reports to the context
    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    context = {
        'violation_reports': violation_reports,
        'user_type': user_type,
        'user_name': user_name,
        'current_page': 'violation_reports',
    }

    return render(request, 'report.html', context)

def users(request):
    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM USERS")
        users = cursor.fetchall()

    if request.method == 'POST':
        user_id_to_toggle_mute = request.POST.get('user_id_to_toggle_mute')
        if user_id_to_toggle_mute:
            with connection.cursor() as cursor:
                # Check if the user is currently muted or not
                cursor.execute("SELECT is_allowed_chat FROM USERS WHERE user_id = %s", [
                               user_id_to_toggle_mute])
                is_muted = cursor.fetchone()[0]

                # Toggle mute status
                new_status = not is_muted
                cursor.execute("UPDATE USERS SET is_allowed_chat = %s WHERE user_id = %s", [
                               new_status, user_id_to_toggle_mute])

            return redirect('users')

    if request.method == 'POST':
        user_id_to_toggle_list = request.POST.get('user_id_to_toggle_list')
        if user_id_to_toggle_list:
            with connection.cursor() as cursor:
                # Check the current listing permission of the user
                cursor.execute("SELECT is_allow_list FROM USERS WHERE user_id = %s", [
                               user_id_to_toggle_list])
                can_list = cursor.fetchone()[0]

                # Toggle the permission
                new_list_status = not can_list
                cursor.execute("UPDATE USERS SET is_allow_list = %s WHERE user_id = %s", [
                               new_list_status, user_id_to_toggle_list])

            return redirect('users')

    # Add violation reports to the context
    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    context = {
        'users': users,
        'user_type': user_type,
        'user_name': user_name,
        'current_page': 'users',
    }

    return render(request, 'users.html', context)

def verify_vehicles(request):
    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    # Check if the request is a POST to update verification status
    if request.method == 'POST':
        listing_id = request.POST.get('listing_id')
        verification_action = request.POST.get('verification_action')

        new_status = None
        listing_status = None
        if verification_action == 'Verify':
            new_status = True
            listing_status = True
        elif verification_action == 'NotVerify':
            new_status = False
            listing_status = False
        # 'Not Started' will be represented by None, so no need for an explicit check

        # # Update the database
        # with connection.cursor() as cursor:
        #     cursor.execute("UPDATE LISTED_VEHICLES SET is_verified = %s WHERE listing_id = %s", [
        #                    new_status, listing_id])
        with connection.cursor() as cursor:
            # The following UPDATE statement sets the is_verified and listing_status columns
            cursor.execute("""
                UPDATE LISTED_VEHICLES
                SET is_verified = %s, listing_status = %s
                WHERE listing_id = %s
            """, [new_status, listing_status, listing_id])
            connection.commit()

        # Redirect to the same page to prevent form resubmission on page refresh
        return redirect('verify_vehicles')

    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM LISTED_VEHICLES")
        vehicles = cursor.fetchall()

    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')

    context = {
        'vehicles': vehicles,
        'user_type': user_type,
        'user_name': user_name,
        'current_page': 'verify_vehicles',
    }

    return render(request, 'verify_vehicles.html', context)

def add_funds(request):
    if request.method == "POST":
        user_id = request.session.get('user_id')
        amount = request.POST.get('amount')

        # Only proceed if the user is logged in as a normal user
        if not user_id or request.session.get('user_type') != 'NORMAL_USER':
            # Redirect to home or show an error
            return HttpResponseRedirect('/')

        try:
            # Update the user's balance in the database
            with connection.cursor() as cursor:
                query = """
                    UPDATE USERS
                    SET balance = balance + %s
                    WHERE user_id = %s;
                """
                cursor.execute(query, (amount, user_id))
                connection.commit()
        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error (e.g., set an error message)

        # Optionally, update the session balance
        request.session['balance'] = str(
            float(request.session.get('balance', 0)) + float(amount))

        # Redirect to the profile page or show a success message
        return HttpResponseRedirect('/profile/')
    else:
        # Redirect or show an error if accessed directly
        return HttpResponseRedirect('/')

def orders(request):
    # enable write commnets
    handle_comment_submission(request)

    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    user_id = request.session.get('user_id', '')
    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    is_seller = request.session.get('is_seller', '')

    # Fetch orders from the database
    orders = []
    if user_id:
        try:
            with connection.cursor() as cursor:
                query = """
                    SELECT vo.order_id, vo.listing_id, lv.image_url, vo.order_price,
                           vo.order_date, vo.is_shipped, vo.tracking_number, vo.is_paid
                    FROM VEHICLE_ORDERS vo
                    INNER JOIN LISTED_VEHICLES lv ON vo.listing_id = lv.listing_id
                    WHERE vo.buyer_id = %s;
                """
                cursor.execute(query, [user_id])
                rows = cursor.fetchall()
                for row in rows:
                    # Check if both is_shipped and is_paid are True
                    is_shipped_and_paid = row[5] == b'\x01' and row[7] == b'\x01'
                    # Check if is_shipped is True
                    is_shipped = row[5] == b'\x01'
                    # Check if is_paid is True
                    is_paid = row[7] == b'\x01'

                    # Append these three boolean values to the row
                    orders.append(
                        row + (is_shipped_and_paid, is_shipped, is_paid))

                print(orders)

        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error

    context = {'orders': orders,
               'user_type': user_type,
               'user_name': user_name,
               'is_seller': is_seller,
               'current_page': 'orders',
               }
    return render(request, 'orders.html', context)

def weekly_reports(request):
    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')

    start_date = None
    end_date = None
    vehicle_start_date = None
    vehicle_end_date = None
    if 'start_date' in request.POST:
        # Default start_date and end_date as None
        request.session['start_date'] = request.POST.get('start_date')
        request.session['end_date'] = request.POST.get('end_date')

    elif 'vehicle_start_date' in request.POST:
        request.session['vehicle_start_date'] = request.POST.get(
            'vehicle_start_date')
        request.session['vehicle_end_date'] = request.POST.get(
            'vehicle_end_date')

    # Retrieve from session if available
    start_date = request.session.get('start_date', '')
    end_date = request.session.get('end_date', '')
    vehicle_start_date = request.session.get('vehicle_start_date', '')
    vehicle_end_date = request.session.get('vehicle_end_date', '')

    # get sales report
    report_data = None
    if start_date and end_date:
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT SUM(order_price) AS TotalSaleAmount, COUNT(order_id) AS TotalNumberOfSales
                FROM VEHICLE_ORDERS
                WHERE order_date BETWEEN %s AND %s
            """, [start_date, end_date])

            report_data = cursor.fetchone()

    popular_vehicles = None
    if vehicle_start_date and vehicle_end_date:
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT
                    listing_id,
                    make,
                    model,
                    year_of_production,
                    number_of_bids
                FROM (
                    SELECT
                        LV.listing_id AS listing_id,
                        LV.make AS make,
                        LV.model AS model,
                        LV.year_of_production AS year_of_production,
                        COUNT(*) AS number_of_bids,
                        DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) AS bid_rank
                    FROM
                        LISTED_VEHICLES AS LV
                    JOIN
                        BIDDINGS AS B ON LV.listing_id = B.listing_id
                    WHERE
                        B.bidding_date BETWEEN %s AND %s
                    GROUP BY
                        LV.listing_id
                ) AS ranked_listings
                WHERE
                    bid_rank <= 5;
            """, [vehicle_start_date, vehicle_end_date])
            popular_vehicles = cursor.fetchall()

    top_sellers = None
    # Fetch top sellers data
    with connection.cursor() as cursor:
        cursor.execute("""
            WITH RankedUsers AS (
                SELECT
                    user_id,
                    user_name,
                    seller_rating,
                    num_of_seller_rating,
                    DENSE_RANK() OVER (ORDER BY seller_rating DESC) AS user_rank
                FROM USERS
                WHERE user_type = 'NORMAL_USER' AND seller_rating != 0
            )
            SELECT
                user_id,
                user_name,
                seller_rating,
                num_of_seller_rating
            FROM RankedUsers
            WHERE user_rank <= 10
            ORDER BY seller_rating DESC, num_of_seller_rating DESC;
        """)
        top_sellers = cursor.fetchall()

    print(start_date)
    print(end_date)
    print(vehicle_start_date)
    print(vehicle_end_date)
    context = {
        'user_type': user_type,
        'user_name': user_name,
        'current_page': 'weekly_reports',
        'report_data': report_data,
        'start_date': start_date,
        'end_date': end_date,
        'top_sellers': top_sellers,
        'popular_vehicles': popular_vehicles,
        'vehicle_start_date': vehicle_start_date,
        'vehicle_end_date': vehicle_end_date,
        'start_date': start_date,
        'end_date': end_date,
    }

    return render(request, 'weekly_reports.html', context)

def other_user_profile(request, other_user_id):
    # enable write commnets
    handle_comment_submission(request)

    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    is_seller = request.session.get('is_seller', '')

    try:
        with connection.cursor() as cursor:
            # Fetch user's user_type
            cursor.execute(
                "SELECT user_type FROM USERS WHERE user_id = %s",
                [other_user_id])
            other_user_type = cursor.fetchone()

            # Check if the other user is ADMIN
            if other_user_type and other_user_type[0] == 'ADMIN':
                return HttpResponseForbidden("Admin User, Access Denied")

            # Fetch user details
            cursor.execute(
                "SELECT user_name, seller_rating, buyer_rating, num_of_seller_rating, num_of_buyer_rating FROM USERS WHERE user_id = %s",
                [other_user_id])
            other_user_details = cursor.fetchone()
            print(other_user_details)

            # Fetch listed vehicles
            if other_user_details:
                cursor.execute(
                    "SELECT listing_id, make, model, year_of_production, image_url FROM LISTED_VEHICLES WHERE seller_id = %s",
                    [other_user_id])
                listed_vehicles = cursor.fetchall()

    except Exception as e:
        print(f"An error occurred: {e}")
        # Return a 404 response for missing user ID
        return HttpResponseNotFound("404 User not found")

    if other_user_details is None:
        # Return a 404 response for missing user ID
        return HttpResponseNotFound("404 User not found")

    context = {
        "other_user_id": other_user_id,
        'user_details': other_user_details,
        'listed_vehicles': listed_vehicles,
        'user_type': user_type,
        'user_name': user_name,
        'is_seller': is_seller,
    }
    return render(request, 'other_user_profile.html', context)

def search_car(request):
    # enable write commnets
    handle_comment_submission(request)

    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    # Fetching data from the session
    user_id = request.session.get('user_id', '')

    # Fetch unique values for dropdowns from the database
    with connection.cursor() as cursor:
        cursor.execute(
            "SELECT DISTINCT make FROM LISTED_VEHICLES ORDER BY make")
        makes = [row[0] for row in cursor.fetchall()]

        cursor.execute(
            "SELECT DISTINCT year_of_production FROM LISTED_VEHICLES ORDER BY year_of_production")
        years = [row[0] for row in cursor.fetchall()]

        cursor.execute(
            "SELECT DISTINCT mileage FROM LISTED_VEHICLES ORDER BY mileage")
        mileages = [row[0] for row in cursor.fetchall()]

        cursor.execute(
            "SELECT DISTINCT price FROM LISTED_VEHICLES ORDER BY price")
        prices = [row[0] for row in cursor.fetchall()]

    error_message = None

    # Fetch filter parameters
    make = request.GET.get('make')
    min_year = request.GET.get('min_year')
    max_year = request.GET.get('max_year')
    min_mileage = request.GET.get('min_mile')
    max_mileage = request.GET.get('max_mile')
    min_price = request.GET.get('min_price')
    max_price = request.GET.get('max_price')

    # Check for invalid filter ranges
    if min_year and max_year and int(min_year) > int(max_year):
        error_message = "Minimum year cannot be greater than maximum year."
    elif min_mileage and max_mileage and int(min_mileage) > int(max_mileage):
        error_message = "Minimum mileage cannot be greater than maximum mileage."
    elif min_price and max_price and int(min_price) > int(max_price):
        error_message = "Minimum price cannot be greater than maximum price."

    # If there's an error, return early with the error message
    if error_message:
        return render(request, 'search_car.html', {'error_message': error_message})

    # Initialize the base query
    query = "SELECT * FROM LISTED_VEHICLES WHERE listing_status = TRUE AND is_verified = TRUE"

    # Initialize the parameters list
    params = []

    # Append conditions to the query based on provided filters
    if make:
        query += " AND make = %s"
        params.append(make)

    if min_year:
        query += " AND year_of_production >= %s"
        params.append(min_year)

    if max_year:
        query += " AND year_of_production <= %s"
        params.append(max_year)

    if min_mileage:
        query += " AND mileage >= %s"
        params.append(min_mileage)

    if max_mileage:
        query += " AND mileage <= %s"
        params.append(max_mileage)

    if min_price:
        query += " AND price >= %s"
        params.append(min_price)

    if max_price:
        query += " AND price <= %s"
        params.append(max_price)

    # Fetch the search term
    search_words = []
    search_term = request.GET.get('search_term', '').strip()

    # Check if search term is not empty
    if search_term:
        search_words = search_term.split()

    # Initialize search_words as an empty list
    search_words = []

    # Fetch the search term
    search_term = request.GET.get('search_term', '').strip()

    # Check if search term is not empty
    if search_term:
        search_words = search_term.split()

        # Build the search query only if there are search words
        search_query = " AND ("
        search_query_parts = []
        for word in search_words:
            search_query_parts.append(
                "(make LIKE %s OR model LIKE %s OR exterior_color LIKE %s OR "
                "vehicle_description LIKE %s OR fuel_type LIKE %s OR CAST(year_of_production AS CHAR) LIKE %s)")
            params.extend(["%" + word + "%"] * 6)

        # Only append if there are parts to the search query
        if search_query_parts:
            search_query += " OR ".join(search_query_parts) + ")"
            query += search_query

    vehicles = []
    with connection.cursor() as cursor:
        cursor.execute(query, params)
        columns = [col[0] for col in cursor.description]
        for row in cursor.fetchall():
            vehicles.append(dict(zip(columns, row)))

    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')
    is_seller = request.session.get('is_seller', '')

    return render(request, 'search_car.html', {
        'user_name': user_name,
        'user_type': user_type,
        'is_seller': is_seller,
        'vehicles': vehicles,
        'makes': makes,
        'years': years,
        'mileages': mileages,
        'prices': prices,
        'error_message': error_message,
        'current_page': 'search_car',
    })

def product_detail(request, listing_id):
    # enable write commnets
    handle_comment_submission(request)

    result = None
    current_bid = None

    with connection.cursor() as cursor:
        # SQL query to join USERS and LISTED_VEHICLES tables
        cursor.execute("""
            SELECT LV.*, U.*
            FROM LISTED_VEHICLES AS LV
            JOIN USERS AS U ON LV.seller_id = U.user_id
            WHERE LV.listing_id = %s
        """, [listing_id])

        result = cursor.fetchone()

    # If no product is found, raise a 404 error
    if not result:
        raise Http404("Product does not exist")

    user_id = request.session.get('user_id', '')
    current_user_id = user_id
    with connection.cursor() as cursor:
        cursor.execute("""
                SELECT bidding_amount FROM BIDDINGS
                WHERE listing_id = %s AND user_id = %s
                ORDER BY bidding_date DESC
                LIMIT 1
            """, [listing_id, current_user_id])
        current_bid = cursor.fetchone()

    # Map the result to a dictionary for easy access in the template
    product_dict = {
        'listing_id': result[0],
        'VIN': result[1],
        'image_url': result[3],
        'vehicle_description': result[4],
        'make': result[5],
        'model': result[6],
        'fuel_type': result[7],
        'year_of_production': result[8],
        'mileage': result[9],
        'price': result[10],
        'exterior_color': result[11],
        'interior_color': result[12],
        'state': result[13],
        'zip_code': result[14],
        'seller_id': result[19],
        'seller_name': result[21],
        'seller_rating': result[24],
        'current_bid': current_bid[0] if current_bid else None,
        'is_active': result[18],
    }
    # print("19")
    # print(result[19])
    # print("18")
    # print(result[18])
    # print("17")
    # print(result[17])
    # print("16")
    # print(result[16])
    # return render(request, 'product_detail.html', {'product': product_dict})
    if request.method == 'POST' and 'bid_amount' in request.POST:
        bid_amount = request.POST.get('bid_amount')
        # Assuming user authentication
        user_id = request.user.id

        # Convert bid amount to a decimal or float as needed
        bid_amount = float(bid_amount)

        # Insert the bid into the database
        with connection.cursor() as cursor:
            cursor.execute("""
                INSERT INTO BIDDINGS (listing_id, user_id, bidding_amount, bidding_date)
                VALUES (%s, %s, %s, NOW())
            """, [listing_id, user_id, bid_amount])
            # If the bid is successfully placed, add a success message
            messages.success(request, 'Bid placed successfully!')
            # Redirect to the same page to display the success message
            return redirect('bid_success', listing_id=listing_id, user_id=request.user.id)

    user_id = request.session.get('user_id', '')
    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')
    is_seller = request.session.get('is_seller', '')
    is_allowed_chat = get_is_allowed_chat(user_id)

    # Add new chat
    seller_id = result[19]
    is_active = result[18]
    current_user_id = request.session.get('user_id', '')

    if add_new_chat(request, listing_id, user_id, seller_id):
        return redirect('product_detail', listing_id=listing_id)

    # Get chat history
    chat_history = get_chat_history(listing_id, current_user_id, seller_id)

    # For GET requests or if the bid placement is not successful, render the page with product details
    return render(request, 'product_detail.html', {
        'product': product_dict,
        'user_name': user_name,
        'user_type': user_type,
        'user_id': user_id,
        'seller_id': seller_id,
        'chat_history': chat_history,
        'is_seller': is_seller,
        'is_allowed_chat': is_allowed_chat,
        'is_active': is_active,
    })

def get_is_allowed_chat(user_id):
    with connection.cursor() as cursor:
        query = "SELECT is_allowed_chat FROM USERS WHERE user_id = %s"
        cursor.execute(query, [user_id])

        row = cursor.fetchone()

        if row is not None:
            # row[0] will be the is_allowed_chat value
            return row[0]
        else:
            return None  # or handle as appropriate

def get_chat_history(listing_id, buyer_id, seller_id):
    chat_messages = []
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT * FROM CHATS
            WHERE listing_id = %s
            AND ((sender_id = %s AND receiver_id = %s)
            OR (sender_id = %s AND receiver_id = %s))
            ORDER BY date ASC
        """, [listing_id, buyer_id, seller_id, seller_id, buyer_id])
        chat_messages = cursor.fetchall()
    return chat_messages

def add_new_chat(request, listing_id, buyer_id, seller_id):
    new_message_added = False
    if request.method == 'POST' and 'new_message' in request.POST:
        sender_id = buyer_id
        receiver_id = seller_id
        new_message = request.POST.get('new_message')
        if new_message:
            with connection.cursor() as cursor:
                cursor.execute("""
                    INSERT INTO CHATS (sender_id, receiver_id, message, date, listing_id)
                    VALUES ( %s, %s, %s, NOW(), %s)
                """, [sender_id, receiver_id, new_message, listing_id])
            new_message_added = True

        return new_message_added

def bid(request, listing_id):
    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    user_id = request.session.get('user_id', '')

    # enable write commnets
    handle_comment_submission(request)

    #  # Check if there is already a winner for this listing
    # is_winner = False
    # with connection.cursor() as cursor:
    #     cursor.execute("""
    #         SELECT is_winner FROM BIDDINGS
    #         WHERE listing_id = %s AND is_winner = TRUE
    #     """, [listing_id])
    #     is_winner = cursor.fetchone() is not None

    if request.method == 'POST':
        # if is_winner:
        #     # If there is already a winner, redirect to an error page or show a message.
        #     return HttpResponse('This listing already has a winning bid and is closed for further bidding.', status=403)

        try:
            bid_amount = float(request.POST['bid_amount'])

            with connection.cursor() as cursor:
                cursor.execute(
                    "SELECT price FROM LISTED_VEHICLES WHERE listing_id = %s", [listing_id])
                product_price = cursor.fetchone()
                if not product_price or bid_amount < product_price[0]:
                    # Handle bid amount lower than product price
                    return render(request, 'error.html', {'error_message': 'Bid amount must be higher than the product price'})

                cursor.execute("SELECT MAX(bidding_id) FROM BIDDINGS")
                max_id_result = cursor.fetchone()
                new_bidding_id = max_id_result[0] + \
                    1 if max_id_result[0] else 1

                cursor.execute("""
                    INSERT INTO BIDDINGS (bidding_id, listing_id, user_id, bidding_amount, bidding_date, is_winner)
                    VALUES (%s, %s, %s, %s, %s, %s)
                """, [new_bidding_id, listing_id, user_id, bid_amount, datetime.now().strftime('%Y-%m-%d %H:%M:%S'), None])
                connection.commit()

            return redirect('product_detail', listing_id=listing_id)
        except IntegrityError as e:
            print(e)
            # Handle the error, perhaps show a message to the user

    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT LV.*, U.*
            FROM LISTED_VEHICLES AS LV
            JOIN USERS AS U ON LV.seller_id = U.user_id
            WHERE LV.listing_id = %s
        """, [listing_id])

        result = cursor.fetchone()

    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')

    product_dict = {
        'user_name': user_name,
        'user_type': user_type,
        'image_url': result[3],
        'make': result[5],
        'model': result[6],
        'price': result[10],

    }

    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')
    is_seller = request.session.get('is_seller', '')

    return render(request, 'bid.html', {
        'product': product_dict,
        'listing_id': listing_id,
        'user_name': user_name,
        'user_type': user_type,
        'is_seller': is_seller,
        'current_page': 'product_detail',

    })

def chatbot(request):
    # enable write commnets
    handle_comment_submission(request)

    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')
    is_seller = request.session.get('is_seller', '')

    return render(request, 'chatbot.html', {
        'user_name': user_name,
        'user_type': user_type,
        'is_seller': is_seller,
        'current_page': 'chatbot',
    })

@csrf_exempt
@require_POST
def execute_query(request):
    data = json.loads(request.body)
    query = data.get('query')

    try:
        with connection.cursor() as cursor:
            cursor.execute(query)
            rows = cursor.fetchall()
            columns = [col[0] for col in cursor.description]
            result = [dict(zip(columns, row)) for row in rows]
            return JsonResponse({'data': result})
    except Exception as e:
        print(f"Query: {query}")
        print(f"Error: {e}")
        return JsonResponse({'error': str(e)}, status=500)

@csrf_exempt
@require_POST
def chat(request):
    data = json.loads(request.body)
    user_message = data.get('message')

    # You need to configure your OpenAI API key
    openai.api_key = api_key

    try:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": user_message}]
        )
        return JsonResponse({'response': response.choices[0].message['content']})
    except Exception as e:
        return JsonResponse({'response': str(e)}, status=500)

# def bid_success(request, listing_id):
#     # need modification
#     with connection.cursor() as cursor:
#         cursor.execute("""
#             SELECT *
#             FROM BIDDINGS
#             WHERE listing_id = %s
#         """, [listing_id])

#         result = cursor.fetchone()

#     # Map the result to a dictionary for easy access in the template
#     bidding_dict = {
#         'bidding_amount': result[3],
#     }

#     return render(request, 'bid.html', {'bidding': bidding_dict})

# # views.py

def buyer_rate_seller(request, order_id):
    user_id = request.session.get('user_id', None)
    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')
    is_seller = request.session.get('is_seller', '')

    # Initialize existing_rating to None
    existing_rating = None

    # Retrieve listing_id, seller_id, and winner_id from the order
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT listing_id, seller_id, buyer_id FROM VEHICLE_ORDERS
            WHERE order_id = %s;
        """, [order_id])
        order_details = cursor.fetchone()

    if order_details:
        listing_id, seller_id, winner_id = order_details

        if user_id != winner_id:
            return HttpResponseForbidden("Access denied: You are not the buyer for this order.")

        # Check if the rating already exists
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT rate FROM RATINGS
                WHERE listing_id = %s AND seller_id = %s AND winner_id = %s
                AND seller_rate_from_winner = TRUE;
            """, [listing_id, seller_id, winner_id])
            existing_rating = cursor.fetchone()
    else:
        return HttpResponseForbidden("Access denied: No Such Order.")

    if request.method == 'POST' and order_details:
        rate = request.POST.get('rate')

        with connection.cursor() as cursor:
            if existing_rating:
                # Update the existing rating
                cursor.execute("""
                    UPDATE RATINGS SET rate = %s
                    WHERE listing_id = %s AND seller_id = %s AND winner_id = %s
                    AND seller_rate_from_winner = TRUE;
                """, [rate, listing_id, seller_id, winner_id])
            else:
                # Insert a new rating
                cursor.execute("""
                    INSERT INTO RATINGS (listing_id, seller_id, winner_id, seller_rate_from_winner, winner_rate_from_seller, rate)
                    VALUES (%s, %s, %s, TRUE, FALSE, %s);
                """, [listing_id, seller_id, winner_id, rate])
            return redirect('orders')  # Redirect to the orders page or another appropriate page

    return render(request, 'buyer_rate_seller.html', {
        'user_name': user_name,
        'user_type': user_type,
        'order_id': order_id,
        'is_seller': is_seller,
        'existing_rating': existing_rating[0] if existing_rating else None,
    })

# post new vehicle listings
def sell_post(request):
    handle_comment_submission(request)

    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    user_id = request.session.get('user_id', '')
    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    is_seller = request.session.get('is_seller', '')
    is_allow_list = request.session.get('is_allow_list', '')

    if request.method == 'POST':
        vin = request.POST.get('vin')
        image_url = request.POST.get('image_url')
        vehicle_description = request.POST.get('vehicle_description')
        make = request.POST.get('make')
        model = request.POST.get('model')
        fuel_type = request.POST.get('fuel_type')
        year_of_production = request.POST.get('year_of_production')
        mileage = request.POST.get('mileage')
        price = request.POST.get('price')
        exterior_color = request.POST.get('exterior_color')
        interior_color = request.POST.get('interior_color')
        state = request.POST.get('state')
        zip_code = request.POST.get('zip_code')
        listing_end_date = request.POST.get('listing_end_date')

        try:
            with connection.cursor() as cursor:
                query = """
                    INSERT INTO LISTED_VEHICLES (VIN, seller_id, image_url, vehicle_description, make, model, fuel_type, year_of_production, mileage, price, exterior_color, interior_color, state, zip_code, listing_start_date, listing_end_date)
                    VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), %s);
                """
                print(query)
                cursor.execute(query, (vin, user_id, image_url, vehicle_description, make, model, fuel_type,
                               year_of_production, mileage, price, exterior_color, interior_color, state, zip_code, listing_end_date))
                connection.commit()
                return redirect('sell_post_success')
        except Exception as e:
            print(f"An error occurred: {e}")
            # Optionally, add feedback for the user here

    return render(request, 'sell_post.html', {
        'user_type': user_type,
        'user_name': user_name,
        'user_id': user_id,
        'is_seller': is_seller,
        "is_allow_list": is_allow_list,
        'current_page': 'sell_post',
    })

# post new vehicle listings successful
def sell_post_success(request):
    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    user_id = request.session.get('user_id', '')
    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    is_seller = request.session.get('is_seller', '')

    return render(request, 'sell_post_success.html', {
        'user_type': user_type,
        'user_name': user_name,
        'user_id': user_id,
        'is_seller': is_seller,
        'current_page': 'sell_post_success',
    })

# Chat with buyer view function

def chat_with_buyer(request):
    handle_comment_submission(request)
    unique_token = int(time.time())

    current_user_id = request.session.get('user_id', '')
    chats = []
    chat_history = []
    selected_listing_id = None
    selected_buyer_name = None
    selected_buyer_id = None
    message_submitted = False
    is_allowed_chat = get_is_allowed_chat(current_user_id)

    # Handling message submission
    if request.method == 'POST' and 'new_message' in request.POST:
        form_token = request.POST.get('form_token')
        if request.session.get('last_form_token') != form_token:
            request.session['last_form_token'] = form_token
            selected_listing_id = request.POST.get('selected_listing_id')
            selected_buyer_name = request.POST.get('selected_buyer_name')
            selected_buyer_id = request.POST.get('selected_buyer_id')
            if add_new_chat(request, selected_listing_id, current_user_id, selected_buyer_id):
                selected_listing_id = request.session.get('selected_listing_id', '')
                sender = request.session.get('sender', '')
                receiver = request.session.get('receiver', '')
                chat_history = get_chat_history(selected_listing_id, sender, receiver)
                redirect('chat_with_buyer')

    # Handling chat selection
    if request.method == 'POST' and 'selected_listing_id' in request.POST:
        selected_listing_id = request.POST.get('selected_listing_id')
        selected_buyer_name = request.POST.get('selected_buyer_name')
        selected_buyer_id = request.POST.get('selected_buyer_id')
        request.session['sender'] = current_user_id
        request.session['receiver'] = selected_buyer_id
        request.session['selected_listing_id'] = selected_listing_id
        chat_history = get_chat_history(selected_listing_id, current_user_id, selected_buyer_id)

    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT
                c.chat_id,
                c.message,
                c.date,
                c.listing_id,
                u.user_name,
                u.user_id
            FROM
                CHATS c
            JOIN
                USERS u ON c.sender_id = u.user_id
            JOIN
                (SELECT
                    MAX(date) as latest_date,
                    listing_id,
                    sender_id
                FROM
                    CHATS
                WHERE
                    receiver_id = %s
                GROUP BY
                    listing_id, sender_id) AS latest_messages ON c.date = latest_messages.latest_date AND c.listing_id = latest_messages.listing_id AND c.sender_id = latest_messages.sender_id
            WHERE
                c.receiver_id = %s AND
                c.listing_id IN
                    (SELECT listing_id FROM LISTED_VEHICLES WHERE seller_id = %s)
            ORDER BY
                c.listing_id, c.sender_id, c.date DESC;
        """, [current_user_id, current_user_id, current_user_id])
        chats = cursor.fetchall()

    user_id = request.session.get('user_id', '')
    print('user_id')
    print(user_id)
    user_type = request.session.get('user_type', '')
    user_name = request.session.get('user_name', '')
    is_seller = request.session.get('is_seller', '')

    return render(request, 'chat_with_buyer.html', {
        'user_type': user_type,
        'user_name': user_name,
        'user_id': user_id,
        'is_seller': is_seller,
        'chats': chats,
        'selected_listing_id': selected_listing_id,
        'selected_buyer_name': selected_buyer_name,
        'selected_buyer_id': selected_buyer_id,
        'chat_history': chat_history,
        'is_allowed_chat': is_allowed_chat,
        'unique_token': unique_token,
        'current_page': 'chat_with_buyer',
    })

def choose_bid(request, listing_id):
    user_email = request.session.get('email', '')
    if user_email:
        update_session(request, user_email)

    user_id = request.session.get('user_id', '')
    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')

    # Check if the listing already has a winning bid
    with connection.cursor() as cursor:
        cursor.execute("""
            SELECT is_winner
            FROM BIDDINGS
            WHERE listing_id = %s AND is_winner = TRUE
        """, [listing_id])
        winner_exists = cursor.fetchone() is not None

    # Fetch bid details using listing_id
    bids_list = []
    if not winner_exists:
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT
                    b.bidding_id,
                    b.user_id,
                    b.bidding_amount,
                    b.bidding_date,
                    u.user_name,
                    u.balance,
                    (u.balance - b.bidding_amount >= 0) AS enough_money_to_pay
                FROM
                    BIDDINGS b
                JOIN
                    USERS u ON b.user_id = u.user_id
                WHERE
                    b.listing_id = %s
                ORDER BY
                    b.bidding_amount DESC
            """, [listing_id])
            bids = cursor.fetchall()
            if bids:
                for bid in bids:
                    bids_list.append({
                        'bidding_id': bid[0],
                        'user_id': bid[1],
                        'bidding_amount': bid[2],
                        'bidding_date': bid[3].strftime("%Y-%m-%d %H:%M:%S"),
                        'user_name': bid[4],
                        'balance': bid[5],
                        'enough_money_to_pay': bid[6]
                    })
    if request.method == 'POST':
        # Get bidding_id from the POST request
        bidding_id = request.POST.get('bidding_id')

        with connection.cursor() as cursor:
            cursor.execute("""
                    SELECT user_id, bidding_amount
                    FROM BIDDINGS
                    WHERE bidding_id = %s;
                """, [bidding_id])
            bid = cursor.fetchone()
        if bid:
            buyer_id, order_price = bid
            seller_id = request.session.get('user_id')

        # update listing_status to not active
        with connection.cursor() as cursor:
            cursor.execute("""
                    UPDATE LISTED_VEHICLES
                    SET listing_status = FALSE
                    WHERE listing_id = %s;
                    """, (listing_id))

        # Insert transaction data for seller
        with connection.cursor() as cursor:
            transaction_type1 = 'receive'
            cursor.execute("""
                    INSERT INTO BALANCE_TRANSACTIONS (user_id, current_balance, date, transaction_type, transaction_amount)
                    VALUES (%s, (SELECT balance FROM USERS WHERE user_id = %s), NOW(), %s, %s);
                    """, (seller_id, seller_id,  transaction_type1, order_price))
        with connection.cursor() as cursor:
            # Insert transaction data for buyer
            transaction_type2 = 'pay'
            cursor.execute("""
                        INSERT INTO BALANCE_TRANSACTIONS (user_id, current_balance, date, transaction_type, transaction_amount)
                        VALUES (%s, (SELECT balance FROM USERS WHERE user_id = %s), NOW(), %s, %s);
                    """, (buyer_id, buyer_id,  transaction_type2, order_price))
        with connection.cursor() as cursor:
            # Update balance in USERS table for seller
            cursor.execute("""
                        UPDATE USERS
                        SET balance = balance + %s
                        WHERE user_id = %s;
                    """, (order_price, seller_id))

            # Update balance in USERS table for buyer
            cursor.execute("""
                        UPDATE USERS
                        SET balance = balance - %s
                        WHERE user_id = %s;
                    """, (order_price, buyer_id))

        with connection.cursor() as cursor:
            cursor.execute("""
                UPDATE BIDDINGS
                SET is_winner = FALSE
                WHERE listing_id = %s;
            """, [listing_id])

        # Set the chosen bid as the winning bid
        with connection.cursor() as cursor:
            cursor.execute("""
                UPDATE BIDDINGS
                SET is_winner = TRUE
                WHERE bidding_id = %s;
            """, [bidding_id])

    # Generate a random tracking number
        tracking_number = random.randint(10000, 99999)
        is_paid = True
        is_shipped = True

        # generate order record
        with connection.cursor() as cursor:
            #     cursor.execute("SELECT MAX(order_id) FROM VEHICLE_ORDERS;")
            #     max_id = cursor.fetchone()[0]
            #     new_order_id = max_id + 1 if max_id else 1

            # Insert order into VEHICLE_ORDERS
            cursor.execute("""
                    INSERT INTO VEHICLE_ORDERS (listing_id, buyer_id, seller_id, order_price, order_date, tracking_number, is_paid, is_shipped)
            VALUES (%s, %s, %s, %s, NOW(), %s, %s, %s);
            """, (listing_id, buyer_id, user_id, order_price, tracking_number, is_paid, is_shipped))
        with connection.cursor() as cursor:
            # Update all biddings to not be the winner
            cursor.execute("""
                    UPDATE BIDDINGS
                    SET is_winner = FALSE
                    WHERE listing_id = %s;
            """, [listing_id])
        with connection.cursor() as cursor:
            # Set the selected bid as the winner
            cursor.execute("""
                    UPDATE BIDDINGS
                    SET is_winner = TRUE
                    WHERE bidding_id = %s;
                """, [bidding_id])

            return redirect('profile')

    # If GET request or no bid has been selected, render the page with bid options
    return render(request, 'choose_bid.html', {
        'winner_exists': winner_exists,
        'bids_list': bids_list,
        'user_type': user_type,
        'user_name': user_name,
        'user_id': user_id,
        'listing_id': listing_id,
    })

def seller_rate_buyer(request, listing_id):
    user_id = request.session.get('user_id', None)
    user_name = request.session.get('user_name', '')
    user_type = request.session.get('user_type', '')

    rating_exists = False
    current_rating = None

    # Track if the user is the seller of the vehicle
    try:
        with connection.cursor() as cursor:
            # Validate if the listing_id exists in LISTED_VEHICLES
            cursor.execute(
                "SELECT COUNT(*) FROM LISTED_VEHICLES WHERE listing_id = %s", [listing_id])
            if cursor.fetchone()[0] == 0:
                return HttpResponseForbidden("Listing not found.")

            # Fetch seller_id for the listing
            cursor.execute(
                "SELECT seller_id FROM LISTED_VEHICLES WHERE listing_id = %s", [listing_id])
            seller_id = cursor.fetchone()[0]

            # Check if the logged-in user is the seller
            if user_id == seller_id:
                is_seller = True
            else:
                return HttpResponseForbidden("You are not the seller.")
    except Exception as e:
        print(f"An error occurred: {e}")

    try:
        with connection.cursor() as cursor:
            # Check if a rating already exists
            cursor.execute("""
                SELECT rate FROM RATINGS
                WHERE listing_id = %s AND seller_id = %s AND seller_rate_from_winner = %s AND winner_rate_from_seller = %s
            """, [listing_id, user_id, False, True])
            result = cursor.fetchone()
            if result:
                rating_exists = True
                current_rating = result[0]
    except Exception as e:
        print(f"An error occurred while checking existing rating: {e}")

    if request.method == 'POST' and not rating_exists:
        rate = request.POST.get('rate')

        try:
            with connection.cursor() as cursor:
                # Fetch the buyer_id from VEHICLE_ORDERS
                cursor.execute(
                    "SELECT buyer_id FROM VEHICLE_ORDERS WHERE listing_id = %s", [listing_id])
                buyer_id = cursor.fetchone()[0]

                # Insert the rating
                cursor.execute("""
                    INSERT INTO RATINGS (listing_id, seller_id, winner_id, seller_rate_from_winner, winner_rate_from_seller, rate)
                    VALUES (%s, %s, %s, %s, %s, %s)
                """, (listing_id, user_id, buyer_id, False, True, rate))

        except Exception as e:
            print(f"An error occurred while inserting the rating: {e}")
            # Handle the error

        return redirect('profile')

    return render(request, 'seller_rate_buyer.html', {
        'user_id': user_id,
        'user_name': user_name,
        'listing_id': listing_id,
        'user_type': user_type,
        'rating_exists': rating_exists,
        'current_rating': current_rating
    })


from typing import List
from arrow import Arrow
import arrow
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy import select, update
from sqlalchemy.ext.automap import automap_base
import sqlalchemy
import numpy as np

from common.enums import TaskTypeEnum
from globalsurgespider.items import StationSurgeListItem
from models.models import StationRealDataSpecific, SpiderTaskInfo, StationStatus, StationRealDataIndex
from sqlalchemy import ForeignKey, Sequence, MetaData, Table
from sqlalchemy import Column, Date, Float, ForeignKey, Integer, text
from sqlalchemy.dialects.mysql import DATETIME, INTEGER, TINYINT, VARCHAR
from core.db import DbFactory
from decorators import timer_count
from conf.settings import TASK_OPTIONS, DB_TABLE_SPLIT_OPTIONS

class StationSurgeRealData:
    """
        海洋站潮位实况
        实现分表功能
    """

    def __init__(self, station_code: str, list_station_realdata: List[StationRealDataSpecific], tid: int):
        self.station_code = station_code
        self.list_station_realdata = list_station_realdata
        self.tid = tid
        self.session = DbFactory().Session
        pass

    @property
    def gmt_start(self) -> Arrow:
        """
            起始时间
        """

        # sorted_list = self.list_station_realdata.sort(_sort)
        sorted_list = sorted(self.list_station_realdata, key=lambda station: station['ts'])
        start_ts: Arrow = sorted_list[0]['ts']
        return arrow.get(start_ts)

    @property
    def gmt_end(self) -> Arrow:
        # 倒叙排列
        sorted_list = sorted(self.list_station_realdata, key=lambda station: station['ts'], reverse=True)
        end_ts: Arrow = sorted_list[0]['ts']
        return arrow.get(end_ts)

    def create_split_tab(self) -> bool:
        """
            创建分表
        """
        is_created = self._check_need_split_tab(to_create=True)
        return is_created
        pass

    def insert_realdata_list(self, to_coverage: bool = False,
                             realdata_list: List[StationSurgeListItem] = []) -> bool:
        """
            判断 当前需要插入的数据是否已经存在于 表中，若存在且 to_coverage = True 则进行覆盖
        :param to_coverage:
        :param realdata_list:
        :return:
        """
        # TODO:[*] 23-03-12 注意此处需要根据插入的数据进行判断，是否需要跨表插入
        # 注意此处为 scrapy.Item 不能直接通过 obj.xx 需要通过 obj.get('xx') 获取
        months_list: List[int] = [temp.get('dt').month for temp in realdata_list]

        def _filter_month(val_realdata, month: int) -> bool:
            return val_realdata.dt.month == month

        months_set = set(months_list)
        # eg {month:int,realdate:List}
        list_distmonth_realdata = []
        # {month:realdata_list}
        dict_distmonth_realdata: dict = {}
        # 若当前爬取的实况跨月，则需要按照不同的月份进行分表
        for temp_month in months_set:
            dict_distmonth_realdata[temp_month] = []
            for temp_realdata in realdata_list:
                if temp_realdata.get('dt').month == temp_month:
                    dict_distmonth_realdata[temp_month].append(temp_realdata)

        for temo_month, temp_realdata_list in dict_distmonth_realdata.items():
            # temp_realdata_dict
            # 取出第一个
            # - 23-04-26 注意 temp_realdata_list[0].get('dt') 是 datetime.datetime ，需要转为 arrow
            now_arrow: Arrow = arrow.get(temp_realdata_list[0].get('dt'))
            tab_name: str = self._get_split_tab_name(now_arrow)
            station_code: str = self.station_code
            if self._check_exist_tab(tab_name):
                pass
            else:
                # 若不存在指定的表，需要创建该表
                self._create_station_realdata_tab(tab_name, now_arrow)
            # 动态更新表名
            self._insert_realdata(tab_name, station_code, realdata_list, to_coverage=to_coverage)

        pass

    @timer_count(10)
    def _insert_realdata(self, tab_name: str, station_code: str, realdata_list: List[StationSurgeListItem],
                         to_coverage: bool = False):
        """
            若 to_coverage = True 则向表中覆盖已存在的数据
        :param realdata_list:
        :param to_coverage:
        :return:
        """
        # dist_stationcodes: set[str] = set([temp.station_code for temp in realdata_list])
        # for dist_station_code in dist_stationcodes:
        #     temp_stationcode_realdata = list(filter(lambda o: o['station_code'] == dist_station_code, realdata_list))
        # 按照 station_code | timestamp | gmt_dt 查询，若存在则批量更新
        # 动态修改当前的表名
        StationRealDataSpecific.__table__.name = tab_name
        # 根据 station_code | gmt_start < gmt_realitime < gmt_end 过滤结果
        # 对于要插入的集合进行遍历
        # -> S1: station_code | gmt_realtime | ts 存在 则 update
        # -> s2: station_code | gmt_realtime | ts 不存在 则 create
        # query = select(StationRealDataSpecific).where(StationRealDataSpecific.station_code == station_code).where(
        #     StationRealDataSpecific.gmt_realtime > self.gmt_start).where(
        #     StationRealDataSpecific.gmt_realtime < self.gmt_end)
        query = select(StationRealDataSpecific).where(StationRealDataSpecific.station_code == station_code)
        # 插入
        print(f'[-]inserting {station_code} realdata,count:{len(realdata_list)}~')

        for temp_realdata in realdata_list:
            # ERROR:
            #     raise AttributeError(f"Use item[{name!r}] to get field value")
            # AttributeError: Use item['surge'] to get field value
            # 判断是否存在
            temp_query = query.where(StationRealDataSpecific.gmt_realtime == temp_realdata['dt'])
            temp_now: datetime = datetime.utcnow()
            surge: float = temp_realdata['surge']
            if np.isnan(surge):
                continue
            try:
                temp_query = self.session.scalars(temp_query).fetchall()
                # TODO:[*] 23-03-03 注意此处的 surge 有可能为 nan 需要加入判断
                # surge:int=
                if len(temp_query) > 0:
                    # update
                    # 在执行如下 update 操作时出错
                    stmt = update(StationRealDataSpecific).where(
                        StationRealDataSpecific.station_code == station_code).where(
                        StationRealDataSpecific.gmt_realtime == temp_realdata['dt']).values(
                        surge=surge)
                    self.session.execute(stmt)\
                        # .execute_options(
                    # synchronize_session="evaluate")
                else:
                    obj_realdata = StationRealDataSpecific(station_code=station_code, surge=surge,
                                                           tid=self.tid,
                                                           gmt_realtime=temp_realdata['dt'],
                                                           ts=temp_realdata['ts'])
                    self.session.add(obj_realdata)
            except Exception as ex:
                # print('[!]func: _insert_realdata ERROR!')
                print(ex.args)

        self.session.commit()
        self.session.close()
        print(f'[-]insert:{station_code} realdata over!')
        # TODO:[-] 23-03-02 写入完当前爬取的 station 潮位后更新 tb:station_status
        # TODO:[*] 23-03-02 此处修改为根据 realdata_list 找到最近的实况时间

        station_status = StationStatusData(station_code)
        station_status.insert(TaskTypeEnum.SUCCESS, self.tid, self.gmt_end.datetime)
        print(f'[-]updated {station_code} status~')
        pass

    def _check_need_split_tab(self, dt: Arrow, to_create: bool = True) -> bool:
        """

            判断是否需要分表，若已存在表是否覆盖
        @param to_create: 是否覆盖
        @return:
        """
        tab_name: str = self._get_split_tab_name(dt)
        is_need = False
        if self._check_exist_tab(tab_name) is False or (self._check_exist_tab(tab_name) and to_create):
            is_need = self._create_station_realdata_tab(tab_name, dt)
        return is_need

    def _get_split_tab_name(self, dt: Arrow) -> str:
        """
            获取分表名称
            按照结束时间生成表名
            eg: gmt_start 2023-02-26 xxx_202302
        @param dt:当前时间
        @return: 分表名称 eg: station_realdata_specific_202302
        """
        tab_base_name: str = DB_TABLE_SPLIT_OPTIONS.get('station').get('tab_split_name')
        tab_dt_name: str = dt.format('YYYYMM')
        tab_name: str = f'{tab_base_name}_{tab_dt_name}'
        return tab_name

    def _check_exist_tab(self, tab_name: str) -> bool:
        """
            判断指定表是否存在
        @param tab_name:
        @return:
        """
        is_exist = False
        auto_base = automap_base()
        db_factory = DbFactory()
        session = db_factory.Session
        engine = db_factory.engine
        auto_base.prepare(engine, reflect=True)
        list_tabs = auto_base.classes
        if tab_name in list_tabs:
            is_exist = True
        return is_exist
        pass

    def _create_station_realdata_tab(self, tab_name: str, dt: arrow.Arrow) -> bool:
        """
            根据 tab_name 创建对应的潮位实况表
        @param tab_name:
        @return:
        """
        is_ok = False
        meta_data = MetaData()
        Table(tab_name, meta_data, Column('id', Integer, primary_key=True),
              Column('is_del', TINYINT(1), nullable=False, server_default=text("'0'"), default=0),
              Column('station_code', VARCHAR(200), nullable=False, index=True), Column('tid', Integer, nullable=False),
              Column('surge', Float, nullable=False),
              Column('ts', Integer, nullable=False),
              Column('gmt_realtime', DATETIME(fsp=6), default=datetime.utcnow, index=True),
              Column('gmt_create_time', DATETIME(fsp=6), default=datetime.utcnow),
              Column('gmt_modify_time', DATETIME(fsp=6), default=datetime.utcnow))
        db_factory = DbFactory()
        session = db_factory.Session
        engine = db_factory.engine
        with engine.connect() as conn:
            # result_proxy = conn.execute(sql_str)
            # result = result_proxy.fetchall()
            try:
                meta_data.create_all(engine)
                is_ok = True
            except Exception as ex:
                print(ex.args)
        # TODO:[-] 23-04-28 还需要插入表 station_realdata_index
        year: int = dt.format('yyyy')
        local_start: arrow.Arrow = arrow.Arrow(year=year, month=dt.format('MM'), day=1, hour=0, minute=0)
        local_end: arrow.Arrow = local_start.shift(month=1)
        gmt_start: arrow.Arrow = local_start.shift(hour=-8)
        gmt_end: arrow.Arrow = local_end.shift(hour=-8)
        tab_index: StationRealDataIndex = StationRealDataIndex(tab_name=tab_name, year=year,
                                                               gmt_start=gmt_start.datetime,
                                                               gmt_end=gmt_end.datetime)
        session.add(tab_index)
        session.commit()
        return is_ok

class SpiderTask:
    def __init__(self, now_utc: Arrow, count_stations: int, task_name: str):
        self.now_utc = now_utc
        self.count_stations = count_stations
        self.task_name = task_name
        self.session = DbFactory().Session
        pass

    def to_db(self) -> int:
        interval: int = TASK_OPTIONS.get('interval')
        task_info: SpiderTaskInfo = SpiderTaskInfo(timestamp=self.now_utc.timestamp(), task_name=self.task_name,
                                                   task_type=TaskTypeEnum.SUCCESS.value,
                                                   spider_count=self.count_stations, interval=interval)
        # with DbFactory().Session as session:
        task_id: int = -1
        try:
            self.session.add(task_info)
            self.session.commit()
        finally:
            task_id = task_info.id
            self.session.close()
        return task_id

class StationStatusData:
    def __init__(self, station_code: str):
        self.station_code = station_code
        self.session = DbFactory().Session

    def insert(self, status: TaskTypeEnum, tid: int, gmt_realtime: datetime):
        """
            插入 station_code 更新 gmt_update_dt | status
        :param status: 状态
        :param tid: tb: task_info id
        :param gmt_realtime: 最后的潮位实况时间
        :return:
        """
        # session = self.session
        now_utc: datetime = datetime.utcnow()
        # step1: 从 tb: station_status 中查找，若存在则update | 不存在则 create
        query = select(StationStatus).where(StationStatus.station_code == self.station_code)
        try:
            temp_first = self.session.scalars(query).fetchall()
            if len(temp_first) > 0:
                # update
                # AttributeError: 'CursorResult' object has no attribute 'execute_options'
                self.session.execute(
                    update(StationStatus).where(StationStatus.station_code == self.station_code).values(
                        status=status.value,
                        gmt_realtime=gmt_realtime, gmt_modify_time=now_utc))
            else:
                obj_status = StationStatus(station_code=self.station_code, tid=tid, status=status.value,
                                           gmt_realtime=gmt_realtime)
                self.session.add(obj_status)
            self.session.commit()
        except Exception as ex:
            print(f'status insert error!msg:{ex.args}')
        # self.session.close()


import logging

import docker

class DockerSubmitter(object):
    @staticmethod
    def run_docker_container(image, command):
        client = docker.from_env()
        container = client.containers.run(image, command, detach=True)
        # Streaming the logs
        for line in container.logs(stream=True):
            logging.info(line.strip())


# %%

import numpy as np
import os
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity

K = 5
n = 3
minihash = False
proj_func_num = 10

work_dir = r'D:\Documents\course\junior\BigDataAna\12-20大作业'
dataset_dir = work_dir + r'\datasets' + '\\'
# print(dataset_dir)

# 获取数据集
raw_data = pd.read_csv(dataset_dir + 'movies.csv')
# raw_data.info()
# print(raw_data.head())
index2Id = {k: v for k, v in enumerate(raw_data['movieId'])}
Id2index = {v: k for k, v in index2Id.items()}

# %%
genres = raw_data['genres'].values

# %%

train_data = pd.read_csv(dataset_dir + 'train_set.csv')
# train_data.info()
train_data.drop('timestamp', axis=1, inplace=True)
# train_data.info()

# %%

mat = train_data.pivot_table(index=['userId'], columns=['movieId'], values=['rating'])
mat.fillna(0, inplace=True)
util_mat = np.array(mat.values)
movie_id2index = {mat.columns[i][1]: i for i in range(util_mat.shape[1])}
index2movie_id = {v: k for k, v in movie_id2index.items()}
user_num = util_mat.shape[0]
movie_num = util_mat.shape[1]

# %%

# 获取tfidf特征矩阵
vectorizer = TfidfVectorizer()
tfidf_mat = vectorizer.fit_transform(genres).toarray()

# %%

from scipy.spatial.distance import pdist, squareform

if minihash:

    # 构建哈希签名矩阵
    mini_mat = np.array(tfidf_mat)
    mini_mat[mini_mat > 0] = 1
    mini_mat[mini_mat <= 0] = 0
    total_movie_num = mini_mat.shape[0]
    feature_num = mini_mat.shape[1]
    sig_mat = np.zeros((proj_func_num, total_movie_num))
    for i in range(proj_func_num):
        # print("pai%d/%d" % (i + 1, proj_func_num))
        pai = [j for j in range(feature_num)]
        np.random.shuffle(pai)
        pai_index = np.argsort(pai)
        for j in range(total_movie_num):
            for index in pai_index:
                if mini_mat[j, pai[index]] == 1:
                    sig_mat[i, j] = pai[index]
                    break

    # %%

    # 计算jiccard相似度 用scipy库
    print('calc sim mat')
    sig_mat = sig_mat.T  # 用行来算比较快
    sim_mat = 1 - squareform(pdist(sig_mat, 'jaccard'))
    print('sim mat cacl finish')
    sim_mat[np.isnan(sim_mat)] = 0

else:
    # 根据特征矩阵计算相似度矩阵
    sim_mat = cosine_similarity(tfidf_mat)
    sim_mat[sim_mat < 0] = 0

# %%

def recommender(mode, userId, util_mat, sim_mat, movie_id):
    userId = userId - 1  # userID 从1开始，矩阵从0开始
    user_num = util_mat.shape[0]
    movie_num = util_mat.shape[1]
    if mode == 1:  # 1为top k推荐 0为预测评分 2为预测单个movie的评分
        user_rate = np.zeros(util_mat.shape[1])
    else:
        user_rate = np.copy(util_mat[userId])

    rate_movie_index = []
    unrate_movie_index = []

    # 找出评了分和没评分的
    for cur_movie in range(movie_num):
        if util_mat[userId, cur_movie] == 0 and mode != 2:
            unrate_movie_index.append(cur_movie)
        else:
            rate_movie_index.append(cur_movie)

    if mode == 2:
        unrate_movie_index.append(movie_id2index[movie_id])

    for cur_movie in unrate_movie_index:
        total = 0
        pred = 0
        for another_movie in rate_movie_index:
            another_movie_rate = util_mat[userId, another_movie]
            if another_movie_rate != 0:
                cur_movie_in_sim_mat = Id2index[index2movie_id[cur_movie]]
                another_movie_in_sim_mat = Id2index[index2movie_id[another_movie]]
                pred += sim_mat[cur_movie_in_sim_mat, another_movie_in_sim_mat] * another_movie_rate
                total += sim_mat[cur_movie_in_sim_mat, another_movie_in_sim_mat]
        user_rate[cur_movie] = pred / total

    if mode == 0 or mode == 2:
        return user_rate
    else: # top k推荐
        user_rate[np.isnan(user_rate)] = 0
        rec_index = np.argsort(user_rate)[-K:]
        movie_id_list = list(mat.columns)
        rec = [movie_id_list[i][1] for i in rec_index]
        return rec

rec = recommender(1, 4, util_mat, sim_mat, 0)
print('recommend for user 671:', rec)

# %%

# 读取测试集
test = pd.read_csv(dataset_dir + 'test_set.csv')
test.drop('timestamp', axis=1, inplace=True)
users, movies, ratings = test['userId'], test['movieId'], test['rating']
# test.info()

# %%

# 开始预测
preds = []
pred_cache = {}
for i in range(len(test)):
    print('%d/%d...' % (i + 1, len(test)))
    rates = recommender(2, users[i], util_mat, sim_mat, movie_id=movies[i])
    preds.append(rates[movie_id2index[movies[i]]])

SSE = np.sum(np.square(preds - ratings))
print(SSE)
# %%


import torch
import onmt
from onmt.Utils import use_gpu

SRC_TMP = ""
TRG_TMP = ""

def load_model(opt):
    checkpoint = torch.load(opt.model, map_location=lambda storage, loc: storage)
    fields = onmt.IO.load_fields(checkpoint['vocab'])
    model_opt = checkpoint['opt']

    model = onmt.ModelConstructor.make_base_model(
                                model_opt, fields, use_gpu(opt), checkpoint)
    return model, fields

def load_batch(fields, opt):
    data = onmt.IO.ONMTDataset(
        TRG_TMP, SRC_TMP, fields,
        use_filter_pred=False)

    data_iter = onmt.IO.OrderedIterator(
        dataset=data, device=opt.gpu,
        batch_size=opt.batch_size * opt.n_best, shuffle=False, repeat=False)

    for batch in data_iter:
        return batch


from src import util
from src import eval
import numpy as np
import pyflux as pf

if __name__ == '__main__':

    p = 4
    q = 4
    h_test = 6

    ts, data = util.load_data("../data/NSW2013.csv", columnName="TOTALDEMAND")
    # ts, data = util.load_data("../data/bike_hour.csv", columnName="cnt")
    # ts, data = util.load_data("../data/TAS2016.csv", columnName="TOTALDEMAND")
    # ts, data = util.load_data("../data/traffic_data_in_bits.csv", columnName="value")
    # ts, data = util.load_data("../data/beijing_pm25.csv", columnName="pm2.5")
    # ts, data = util.load_data("../data/pollution.csv", columnName="Ozone")

    train, test = util.divideTrainTest(data)
    # train = train[:1000]
    # test = test[:14]
    print("train shape is", train.shape)
    print("test shape is", test.shape)
    history = [x[0] for x in train]
    predictions = []
    realTestY = []

    for t in range(len(test) - h_test):

        model = pf.ARIMA(data=np.array(history), ar=p, ma=q, family=pf.Normal())
        model.fit(method="MLE")

        output = model.predict(h_test, intervals=False)

        yhat = output.values.flatten().tolist()

        obs = test[t : t + h_test].flatten()

        realTestY.append(obs)
        predictions.append(yhat)
        history.extend(test[t])
        print("t:", t+1)
        print("predict:", yhat)
        print('expected:', obs)

    realTestY = np.array(realTestY).reshape(-1, h_test)
    predictions = np.array(predictions).reshape(-1, h_test)
    MAE = eval.calcMAE(realTestY, predictions)
    RMSE = eval.calcRMSE(realTestY, predictions)
    MAPE = eval.calcSMAPE(realTestY, predictions)
    print('Test MAE: %.8f' % MAE)
    print('Test RMSE: %.8f' % RMSE)
    print('Test SMAPE: %.8f' % MAPE)

    # plot
    # pyplot.plot(test)
    # pyplot.plot(predictions, color='red')
    # pyplot.show()


from sympy import *
from IPython.display import display, Latex
import control.matlab as ml

def printer(var_name, var_value, unit):
     return display(Latex("${name} = {var:.4f}\ {unit}$".format(var=var_value,name=var_name,unit=unit)))

# Approximation for Eckfreq for PT1 TF #
def find_eckfreq_PT1(mag,omega):
    index = 0

    for i in ml.mag2db(mag):
        if i <= (max(ml.mag2db(mag))-3):
            display(Latex(f"""$$\\text{{at index: }} {index}$$
                                $$\\text{{mag is: }} {ml.mag2db(mag[index])}\ dB$$
                                $$\\text{{freq is: }} {omega[index]}\ Hz$$"""))
            break
        index+=1
    return index, ml.mag2db(mag[index]), omega[index]

# Approximation for Eckfreq for PT2 TF #
def find_eckfreq_PT2(tf):
    eck = sqrt(tf.pole()[1]*tf.pole()[0]).evalf()
    return display(Latex("$\\omega_0 = {eck:.4f}\ 1/s$".format(eck=eck)))

# Analyzes TF and gets Eckfreq D and K parameters #
def analyze_tf(tf):
    den = tf.den[0][0]
    num = tf.num[0][0]
    if len(den) == 3:
        type_ = "PT2"
        if den[0] == 1:
            omega_calc = sqrt(den[2])
            daempfung = den[1]/(omega_calc*2)
        else:
            vorfaktor = den[0]
            omega_calc = sqrt(den[2]/vorfaktor)
            daempfung = den[1]/(omega_calc*2)
        if len(num) == 1:
            K = num[0]/omega_calc**2
        return omega_calc, K, daempfung

    if len(den) == 2:
        type_ = "PT1"
        daempfung = None
        if den[1] == 1:
            omega_calc = 1/(den[0])
        else:
            vorfaktor = den[1]
            omega_calc = 1/(den[1]/vorfaktor)
        if len(num) == 1:
            K = num[0]
        return omega_calc, K
    # printer("\\omega_0", omega_calc, "1/s")
    # return {
    #         type_:
    #             {
    #                 "omega_0":omega_calc,
    #                 "D":daempfung,
    #                 "K":K
    #             }
    #         }