from mujoco_py import load_model_from_xml, MjSim, MjViewer

import math
import os
import time
import sys
import util
from agent import *
#from learningAgent import *

class Simulator:

	def __init__(self, model, agent, game):
		self.sim = MjSim(model)
		try:
			self.viewer = MjViewer(self.sim)
		except:
			pass
		self.agent = agent
		self.timer = 0
		self.data = {}
		self.averageDistance = 0
		self.totDistance = 0
		self.totReward = 0
		self.rewardtimes = 0
		self.resetTimes = 0
		self.unit_dis = 0.5
		self.data['max_distance'] = 0
		self.data['m_distance'] = 0
		self.data['distance'] = 0
		self.prog = 0
		self.tot_prog = 0
		self.sit_time = 0
		self.sit_height = 1.1
		self.game = game

	def _updateData(self, key, value):
		'''Update the value for key and velocity for key.'''

		v_key = key + '_v'

		if key in self.data:
			self.data[v_key] = value - self.data[key]
		else:
			self.data[v_key] = 0

		self.data[key] = value

	def takeAction(self, action):

    	#0 is leftUp, 1 is rightUp, 2 is leftLow, 3 is rightLow
		'''
    	if self.data['switch'] :
    		self.sim.data.ctrl[0] = 0
			self.sim.data.ctrl[1] = 0
    		if action == 'Q':
				self.sim.data.ctrl[0] = 1
				self.sim.data.ctrl[1] = -1
			if action == 'P':
				self.sim.data.ctrl[0] = -1
				self.sim.data.ctrl[1] = 1
		else:
			self.sim.data.ctrl[2] = 0
			self.sim.data.ctrl[3] = 0
		'''

		#print(action)

		self.sim.data.ctrl[0] = action[0]
		self.sim.data.ctrl[1] = action[1]
		self.sim.data.ctrl[2] = action[2]
		self.sim.data.ctrl[3] = action[3]
		return

		self.sim.data.ctrl[0] = 0
		self.sim.data.ctrl[1] = 0
		self.sim.data.ctrl[2] = 0
		self.sim.data.ctrl[3] = 0

		action = [(action, 10000)]

		#print(action)

		for act, rate in action:

			if act == 'Q':
				self.sim.data.ctrl[0] += rate
				self.sim.data.ctrl[1] -= rate
			elif act == 'W':
				self.sim.data.ctrl[2] -= rate
			elif act == 'O':
				self.sim.data.ctrl[3] -= rate
			elif act == 'P':
				self.sim.data.ctrl[0] -= rate
				self.sim.data.ctrl[1] += rate
			elif act == 'E':
				self.sim.data.ctrl[2] += rate
			elif act == 'I':
				self.sim.data.ctrl[3] += rate
			elif act == 'R':
				self.reset()

	def reset(self):

		self.resetTimes += 1;
		self.totDistance += self.data['max_distance']
		self.averageDistance = self.totDistance/self.resetTimes;
		self.data['max_distance'] = 0
		self.data['distance'] = 0
		self.data['m_distance'] = 0
		self.sim.reset()
		self.prog = 0
		self.timer = 0
		self.tot_prog = 0

	def resetTimer(self):
		self.timer = 0
		self.averageDistance = 0
		self.totDistance = 0
		self.totReward = 0
		self.rewardtimes = 0
		self.resetTimes = 0

	def step(self):
		self.sim.step()
		self.timer += 1

	def display(self):
		self.viewer.render()

	def isDeath(self):
		return self.data['height']<0.3 or abs(self.data['angle_rotate'])>2 or self.sit_time > 3000 #\
			   #or abs(self.data['angle_joint_leftup']) > 2 \

			   #or abs(self.data['angle_joint_rightup']) > 2 \

			   #or abs(self.data['angle_joint_leftup'] - self.data['angle_joint_rightup']) > 3

	def updateData(self):

		rx, ry, rz = self.sim.data.get_geom_xpos('bodyRight')
		ux, uy, uz = self.sim.data.get_geom_xpos('bodyUp')
		cx, cy, cz = self.sim.data.get_geom_xpos('body')
		flx, fly, flz = self.sim.data.get_geom_xpos('leftFeet')
		frx, fry, frz = self.sim.data.get_geom_xpos('rightFeet')

		self.data['height'] = cz

		if cz < self.sit_height:
			self.sit_time += 1
		else:
			self.sit_time = 0

		self.data['distance'] = cx

		if abs(self.data['m_distance'] - self.data['distance']) > self.unit_dis :
			if self.data['m_distance'] > self.data['distance']:
				self.prog -= 1
				self.tot_prog -= 1
			else:
				self.prog += 1
				self.tot_prog += 1
			self.data['m_distance'] = self.data['distance']

		self.data['max_distance'] = max(self.data['max_distance'], (int)(self.data['distance']/self.unit_dis))

		#self.data['right_body'] = self.sim.data.get_geom_xpos('bodyRight')
		#self.data['up_body'] =  self.sim.data.get_geom_xpos('bodyUp')
		#self.data['body_pos'] = self.sim.data.get_geom_xpos('body')
		self.data['angle_bend'] = math.atan2(util.norm2d((ux-cx,uy-cy)),uz-cz)
		self.data['angle_rotate'] = math.atan2(ry-cy,rx-cx) - math.pi/2
		self.data['velocity'] = self.sim.data.get_geom_xvelp('body')[0]
		self.data['vx'] = self.sim.data.get_geom_xvelp('body')[0]
		self.data['vy'] = self.sim.data.get_geom_xvelp('body')[1]
		self.data['vz'] = self.sim.data.get_geom_xvelp('body')[2]
		self.data['angle_joint_rightup'] = self.sim.data.get_joint_qpos('rightUp')
		self.data['angle_joint_rightup_v'] = self.sim.data.get_joint_qvel('rightUp')
		self.data['angle_joint_leftup'] = self.sim.data.get_joint_qpos('leftUp')
		self.data['angle_joint_leftup_v'] = self.sim.data.get_joint_qvel('leftUp')
		self.data['angle_joint_rightdown'] = self.sim.data.get_joint_qpos('rightLow')
		self.data['angle_joint_rightdown_v'] = self.sim.data.get_joint_qvel('rightLow')
		self.data['angle_joint_leftdown'] = self.sim.data.get_joint_qpos('leftLow')
		self.data['angle_joint_leftdown_v'] = self.sim.data.get_joint_qvel('leftLow')
		self.data['avg_velocity'] = self.data['distance']/(self.timer+1e-5)*300
		self.data['flx'] = flx - cx
		self.data['fly'] = fly - cy
		self.data['flz'] = flz - cz
		self.data['frx'] = frx - cx
		self.data['fry'] = fry - cy
		self.data['frz'] = frz - cz

		self.data['timer'] = self.timer

		#print(self.data['angle_joint_leftup_v'])

	def getState(self):
		return self.data.copy()

	'''def getDiscreteState(self):
		bend_unit = 0.5
		rotate_unit = 0.5
		angleup_unit = 0.3
		angledown_unit = 0.8
		angleup_max = 8
		angledown_max = 3
		anglevup_unit = 3
		anglevdown_unit =4
		anglevup_max = 4
		anglevdown_max = 2

		state = {}
		state['bend_state'] = math.floor(self.data['angle_bend']/bend_unit)
		state['rotate_state'] = math.floor(self.data['angle_rotate']/rotate_unit)

		state['rightup_ang'] = util.chunk(self.data['angle_joint_rightup'], angleup_unit, angleup_max)
		state['rightdown_ang'] = util.chunk(self.data['angle_joint_rightdown'],
									angledown_unit, angledown_max)
		state['leftup_ang'] = util.chunk(self.data['angle_joint_leftup'], angleup_unit, angleup_max)
		state['leftdown_ang'] = util.chunk(self.data['angle_joint_leftdown'],
									angledown_unit, angledown_max)

		state['rightup_angv'] = util.chunk(self.data['angle_joint_rightup_v'],
											anglevup_unit, anglevup_max)
		state['rightdown_angv'] = util.chunk(self.data['angle_joint_rightdown_v'],
											anglevdown_unit, anglevdown_max)
		state['leftup_angv'] = util.chunk(self.data['angle_joint_leftup_v'],
											anglevup_unit, anglevup_max)
		state['leftdown_angv'] = util.chunk(self.data['angle_joint_leftdown_v'],
											anglevdown_unit, anglevdown_max)

		state['switch'] = self.timer % 2
		#print(state)

		return tuple([x for x in state.items()])'''

	def getReward(self, action):
		reward = 0
		if self.isDeath():
			reward = -50
		elif action == 'R':
			reward = -99
		else:
			#print(self.data['distance'], self.data['m_distance'])
			reward = max(100 * self.prog, 10 * self.prog)
			# reward > 0 and self.tot_prog >0:
			#	reward *= self.tot_prog
			self.prog = 0
			'''if reward > 0 :
				print("Go!!")
			if reward <0:
				print("Dawm!!")'''

		reward = reward
		#print(reward)
		self.totReward += reward
		self.rewardtimes += 1

		return reward

	def printData(self):

		for key in self.data:
			print(key, ':', self.data[key])


"""
    File created by Ahmed Halat, Armand Sarkezians, and Mohamed Halat
    CSCD58 Fall 2022

    This file contains the IPv4 header structure.
"""

from structures import Ethernet
from struct import unpack
from helpers.map_ip_and_mac import get_ip

class IPv4(Ethernet, dict):
    def __init__(self, raw_data):
        super().__init__(raw_data)

        ver_hdr_len = raw_data[0]
        ver = ver_hdr_len >> 4
        hdr_len = (ver_hdr_len & 15) * 4

        ttl, protocol, src, dest = unpack('! 8x B B 2x 4s 4s', raw_data[:20])

        data = raw_data[hdr_len:]

        src = get_ip(src)
        dest = get_ip(dest)

        self.version = ver
        self.ttl = ttl
        self.protocol = protocol
        self.src = src
        self.dest = dest
        self.raw_data = data

        dict.__init__(self, version=self.version, ttl=self.ttl, protocol=self.protocol, src=self.src, dest=self.dest, raw_data=self.raw_data, name='IPv4')

    def __str__(self):
        s = '----------IPv4----------'
        s += 'Version: {}, TTL: {}, Protocol: {}\n'.format(self.version, self.ttl, self.protocol)
        s += 'Source: {}, Destination: {}\n'.format(self.src, self.dest)
        s += 'Data: {}\n'.format(self.raw_data)
        s += '\n'
        return s


#!/usr/bin/env python3
import pathlib, acts, acts.examples
from acts.examples.simulation import (
    addParticleGun,
    MomentumConfig,
    EtaConfig,
    ParticleConfig,
    addPythia8,
    addFatras,
    ParticleSelectorConfig,
    addDigitization,
)
from acts.examples.reconstruction import (
    addSeeding,
    SeedingAlgorithm,
    TruthSeedRanges,
    addKalmanTracks,
    addCKFTracks,
    CKFPerformanceConfig,
    TrackSelectorRanges,
    addAmbiguityResolution,
    AmbiguityResolutionConfig,
    addVertexFitting,
    VertexFinder,
)
from common import getOpenDataDetectorDirectory
from acts.examples.odd import getOpenDataDetector

import uproot

u = acts.UnitConstants
geoDir = getOpenDataDetectorDirectory()

oddMaterialMap = geoDir / "data/odd-material-maps.root"
oddDigiConfig = geoDir / "config/odd-digi-smearing-config.json"
oddSeedingSel = geoDir / "config/odd-seeding-config.json"
oddMaterialDeco = acts.IMaterialDecorator.fromFile(oddMaterialMap)

detector, trackingGeometry, decorators = getOpenDataDetector(
    geoDir, mdecorator=oddMaterialDeco
)
field = acts.ConstantBField(acts.Vector3(0.0, 0.0, 2.0 * u.T))

multiplicity=10
particles=4

data = {
  VertexFinder.Iterative: {
    'event_vertices': [],
    'reco_vertices': []
  },
  VertexFinder.AMVF: {
    'event_vertices': [],
    'reco_vertices': []
  },
}

vertexing_label = {
  VertexFinder.Iterative: 'IVF',
  VertexFinder.AMVF: 'AMVF',
}

outputDir = pathlib.Path.cwd() / "output"

for vertexing in [VertexFinder.Iterative, VertexFinder.AMVF]:
  rnd = acts.examples.RandomNumbers(seed=42)

  s = acts.examples.Sequencer(events=1, skip=1, numThreads=-1, outputDir=str(outputDir))

  addParticleGun(
      s,
      MomentumConfig(1.0 * u.GeV, 10.0 * u.GeV, transverse=True),
      EtaConfig(-3.0, 3.0, uniform=True),
      ParticleConfig(particles, acts.PdgParticle.eMuon, randomizeCharge=True),
      vtxGen=acts.examples.GaussianVertexGenerator(
          stddev=acts.Vector4(12.5 * u.um, 12.5 * u.um, 55.5 * u.mm, 0.0 * u.ns),
          mean=acts.Vector4(0, 0, 0, 0),
      ),
      multiplicity=multiplicity,
      rnd=rnd,
      outputDirRoot=outputDir,
  )

  addFatras(
      s,
      trackingGeometry,
      field,
      rnd=rnd,
      outputDirRoot=outputDir,
  )

  addDigitization(
      s,
      trackingGeometry,
      field,
      digiConfigFile=oddDigiConfig,
      rnd=rnd,
      outputDirRoot=outputDir,
  )

  """
  addSeeding(
      s,
      trackingGeometry,
      field,
      #ParticleSmearingSigmas(d0=20*u.um, d0PtA=0, d0PtB=0, z0=20*u.um, z0PtA=0, z0PtB=0),
      seedingAlgorithm=SeedingAlgorithm.TruthSmeared,
      geoSelectionConfigFile=oddSeedingSel,
      outputDirRoot=outputDir,
  )

  addKalmanTracks(
      s,
      trackingGeometry,
      field,
  )
  """

  addSeeding(
      s,
      trackingGeometry,
      field,
      geoSelectionConfigFile=oddSeedingSel,
      outputDirRoot=outputDir,
  )

  addCKFTracks(
      s,
      trackingGeometry,
      field,
      CKFPerformanceConfig(ptMin=0.0, nMeasurementsMin=6),
      TrackSelectorRanges(pt=(1.0 * u.GeV, None), absEta=(None, 3.0), removeNeutral=True),
      outputDirRoot=outputDir,
  )

  addAmbiguityResolution(
      s,
      AmbiguityResolutionConfig(maximumSharedHits=3),
      CKFPerformanceConfig(ptMin=0.0, nMeasurementsMin=6),
      outputDirRoot=outputDir,
  )

  addVertexFitting(
      s,
      field,
      TrackSelectorRanges(pt=(0.5 * u.GeV, None), loc0=(-4.0 * u.mm, 4.0 * u.mm), absEta=(None, 3.0), removeNeutral=True),
      vertexFinder=vertexing,
      outputDirRoot=outputDir,
      logLevel=acts.logging.Level.VERBOSE,
  )

  s.run()

  particles_data = uproot.open(outputDir / 'particles.root')
  particles_data = particles_data['particles'].arrays(library='pd')
  for _,(x,y,z,px,py,pz) in particles_data[['vx','vy','vz','px','py','pz']].iterrows():
    data[vertexing]['event_vertices'].append((x,y,z,px,py,pz))

  vertex_data = uproot.open(outputDir / 'performance_vertexing.root')
  vertex_data = vertex_data['vertexing'].arrays(library='pd')
  for _,(x,y,z) in vertex_data[['recoX','recoY','recoZ']].iterrows():
    data[vertexing]['reco_vertices'].append((x,y,z))

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec

fig = plt.figure()
gs = gridspec.GridSpec(2, 1, height_ratios=[1, 1])

ax0 = plt.subplot(gs[0])
ax0.set_xlabel('z')
ax0.set_ylabel('x')

ax1 = plt.subplot(gs[1], sharex=ax0)
ax1.set_xlabel('z')
ax1.set_ylabel('y')

vertices = np.array(data[VertexFinder.Iterative]['event_vertices'])
p = vertices[:,3:6] / np.linalg.norm(vertices[:,3:6], axis=-1)[:,np.newaxis]
ax0.quiver(vertices[:,2], vertices[:,0], p[:,2], p[:,0], angles='uv', units='inches')
ax1.quiver(vertices[:,2], vertices[:,1], p[:,2], p[:,1], angles='uv', units='inches')
ax0.scatter(vertices[:,2], vertices[:,0], label='true')
ax1.scatter(vertices[:,2], vertices[:,1], label='true')

for vertexing in [VertexFinder.Iterative, VertexFinder.AMVF]:
  vertices = np.array(data[vertexing]['reco_vertices'])
  if len(vertices) == 0:
    continue
  ax0.scatter(vertices[:,2], vertices[:,0], label=vertexing_label[vertexing])
  ax1.scatter(vertices[:,2], vertices[:,1], label=vertexing_label[vertexing])

plt.legend()
plt.show()


from django.http import HttpResponse
from django.template.loader import get_template
from django.conf import settings
import os
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
import qrcode

def generate_order_pdf(order):
    # Generate a unique filename for the PDF
    filename = f"order_{order.order_number}.pdf"
    filepath = os.path.join(settings.MEDIA_ROOT, "pdf", filename)
    app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    relative_filepath = os.path.relpath(filepath, app_root)

    # Create a canvas and set the PDF properties
    c = canvas.Canvas(filepath, pagesize=letter)

    # Add content to the PDF
    c.setFont("Helvetica", 12)
    c.drawString(1 * inch, 10 * inch, f"Order Number: {order.order_number}")
    c.drawString(1 * inch, 9 * inch, f"Recipient Name: {order.recipient_name}")
    c.drawString(1 * inch, 8 * inch, f"Recipient Address: {order.recipient_address}")
    c.drawString(1 * inch, 7 * inch, f"Business: {order.sender.name}")
    c.drawString(1 * inch, 6 * inch, f"Region: {order.recipient_region.name}")

    # Generate the QR code with order details
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )
    qr.add_data(order.order_number)

    qr.make(fit=True)
    qr_img = qr.make_image(fill_color="black", back_color="white")
    qr_img.save(
        os.path.join(settings.MEDIA_ROOT, "qr_codes", f"order_{order.order_number}.png")
    )

    # Draw the QR code on the PDF
    qr_img_path = os.path.join(
        settings.MEDIA_ROOT, "qr_codes", f"order_{order.order_number}.png"
    )
    c.drawImage(qr_img_path, 1 * inch, 4 * inch, width=2 * inch, height=2 * inch)

    # Save and close the canvas
    c.showPage()
    c.save()

    # Return the file path for further use
    return relative_filepath


import numpy as np
from scipy.special import expit
from sklearn import preprocessing
from functools import reduce
from total_arrange import get_data

if __name__ == '__main__':
    # get training and test data
    A1, Y, Ate, Yte = get_data('dataset3.csv', 5, 'hot')
    A1 = preprocessing.scale(A1)
    Ate = preprocessing.scale(Ate)

    for k in range(9, 10):
        np.random.seed(7)
        eta = 0.01
        nh = [20] * 11
        W = [0] * 11
        G = [0] * 11
        Ao = [0] * 11
        d = [0] * 11

        # h1(hidden) x m(input)
        W[0] = 2 * np.random.random((nh[0], 136)) - 1
        G[0] = np.zeros((nh[0], 136))

        for i in range(1, 10):
            W[i] = 2 * np.random.random((nh[i], nh[i - 1])) - 1
            G[i] = np.zeros((nh[i], nh[i - 1]))

        W[10] = 2 * np.random.random((4, nh[9])) - 1
        G[10] = np.zeros((4, nh[9]))

        for i in range(1, 10000):
            # expit(x) = 1 / (1 + exp(-x))
            eta = 1 / i
            # A2
            Ao[0] = expit(np.dot(W[0], A1.T))

            for j in range(1, 11):
                Ao[j] = expit(np.dot(W[j], Ao[j - 1]))

            d[10] = Ao[10] - Y.T

            for j in range(9, -1, -1):
                d[j] = np.dot(W[j + 1].T, d[j + 1]) * (Ao[j] * (1 - Ao[j]))

            for j in range(10, 0, -1):
                # print(G[j].shape)
                # print(d[j].shape)
                # print(Ao[j-1].shape)
                G[j] = G[j] + np.dot(d[j], Ao[j - 1].T)
            G[0] = G[0] + np.dot(d[0], A1)

            for j in range(1, 11):
                W[j] = W[j] - (eta * G[j])

        """
        corr = reduce(tr, np.rint(A3.T) - Y, 0)
        ratio = corr / Y.shape[0]

        print("Number of hidden units: {0}".format(nh))
        print("Training accuracy: {0}%".format( ratio * 100))

        # running the test data on the neural network
        A2te = expit(np.dot(W1, Ate.T))
        A3te = expit(np.dot(W2, A2te))
        corr = reduce(tr, np.rint(A3te.T) - Yte, 0)
        ratio = corr / Yte.shape[0]

        print("Test accuracy: {0}%\n".format( ratio * 100))
        """
        diff = np.rint(Ao[10].T) - Y
        count_correct = 0
        print(diff)
        for i in diff:
            if np.array_equal(i, [0, 0, 0, 0]):
                count_correct += 1
        print(count_correct)
        """
        print('[')
        for i in range(W2.shape[0]):
            print('[',end=' ')
            for j in range(W2.shape[1]):
                print(W2[i, j], end=' ')
            print('],', end=' ')
        print(']')
        """


from flask import *
import sqlite3
import os

app = Flask(__name__)

path = os.getcwd()
db = path + "\data\data.db"
print("数据库链接成功！")

def connect_db():
    return sqlite3.connect(db)

@app.route('/')
def index():
    return render_template('home.html')

@app.route('/db')
def data():
    conn = connect_db()
    conn.row_factory = sqlite3.Row
    cur = conn.cursor()
    cur.execute("select lowPrice,highPrice,avgPrice,pubDate from vegetables where pubDate >= ('2022-09-25 00:00:00')")
    rows = cur.fetchall()
    return render_template("vegetables.html", rows=rows)

@app.route('/test')
def test():
    conn = connect_db()
    conn.row_factory = sqlite3.Row
    cur1 = conn.cursor()
    cur1.execute("select lowPrice,highPrice,avgPrice from vegetables where prodName=('大白菜')")
    rows1 = cur1.fetchall()
    return render_template("test.html", rows=rows1)

if __name__ == '__main__':
    app.run(debug=True)


from django.shortcuts import render,redirect
from users.forms import UserRegisterForm
from django.contrib.auth.decorators import login_required
from django.contrib import messages
# Create your views here.

def register(request):
    if request.method=="POST":
        form=UserRegisterForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request,'You have been successfully registered')
            return redirect('blog-home')

    else:
        form=UserRegisterForm()

    return render(request,'users/register.html',{'form':form})
 #Yet to be developed
'''@login_required
def profile(request):
    return render(request,'users/profile.html')'''


class BuySellStock:
    #   Recursive method
    # def buySell(self, prices, run, total, i):
    #     if len(prices) == 0 or len(prices) == 1:
    #         return 0
    #     elif i > len(prices) - 1:
    #         return total
    #     else:
    #         dif = prices[i] - prices[i - 1]
    #         if run + dif > dif:
    #             return self.buySell(prices, run + dif, max(total, run + dif), i+1)
    #         else:
    #             return self.buySell(prices, dif, max(total, dif), i+1)

    #   DP Method
    def buySellDP(self, prices):
        running_total = maxSum = 0
        while len(prices) > 1:
            dif = prices[-1] - prices[-2]
            if running_total + dif > dif:
                running_total += dif
                prices = prices[:-1]
            else:
                running_total = dif
                prices = prices[:-1]

            maxSum = max(running_total, maxSum)

        return maxSum if maxSum > 0 else 0

p = [2, 1, 2, 1, 0, 1, 2]

print(BuySellStock().buySellDP(p))


class Solution(object):
    def longestValidParentheses(self, s):
        n = len(s)
        if n==0:
            return 0
        maxLen = 0;
        isValid = [[False for x in range(n)] for y in range(n)]

        for l in (2 ** n for n in range(1, int(n ** (1 / 2) + 1))):
            for start in range(n-l+1):
                if (s[start]=='(' and s[start+l-1]==')') and (l==2 or isValid[start+1][start+l-2]):
                    isValid[start][start+l-1] = True
                    maxLen = max(maxLen, l)

        return maxLen

text = '()()'
s = Solution()
print(s.longestValidParentheses(text))

DP/Longest Palindrome/Longest Valid Parenthesis.py


from aiogram import types
from aiogram.dispatcher import filters

from database.UsersDatabaseManager import UsersDatabaseManager
from loader import dp, bot
from utility import format_name, rank_degrees, karma_degrees

@dp.message_handler(commands='top_spamers')
@dp.message_handler(filters.Text(equals='!Спамеры', ignore_case=True))
async def spamers_repr(message: types.Message):
    with UsersDatabaseManager() as db_worker:
        data = db_worker.get_messages(message.chat.id)
    out = 'Топ пользователей по написанным сообщениям: \n'
    num = 1
    for user in data:
        count = int(user[2])
        rank = ""
        for c, r in rank_degrees.items():
            if count > c:
                rank = r
                break
        out += f'{num}. {rank} {format_name(user[0], user[1])} - {count} сообщений\n'
        num += 1
    await bot.send_message(message.chat.id, out)
    await bot.delete_message(message.chat.id, message.message_id)

@dp.message_handler(commands='top_karma')
@dp.message_handler(filters.Text(equals='!Карма', ignore_case=True))
async def karma_repr(message: types.Message):
    with UsersDatabaseManager() as db_worker:
        data = db_worker.karma_repr(message.chat.id)
    out = 'Топ кармы: \n'
    num = 1
    for user in data:
        karma = int(user[2])
        rank = ""
        for c, r in karma_degrees.items():
            if karma < c:
                rank = r
                break
        out += f'{num}. {rank} {format_name(user[0], user[1])} - {karma}\n'
        num += 1
    await bot.send_message(message.chat.id, out)
    await bot.delete_message(message.chat.id, message.message_id)


import logging
import warnings
from pathlib import Path
from os import getenv as env

import dotenv
from privex.helpers import empty, empty_if, env_bool, env_int, env_csv, DictObject
from privex.helpers import settings as pvx_settings

try:
    from rich.logging import RichHandler
except Exception as rxe:
    warnings.warn(f"Failed to import rich.logging.RichHandler. Reason: {type(rxe)} - {rxe!s}", ImportWarning)
    RichHandler = None

dotenv.load_dotenv()

MINUTE = 60
HOUR = MINUTE * 60
DAY = HOUR * 24
WEEK = DAY * 7
MONTH = DAY * 30
YEAR = DAY * 365

cf = DictObject()
APP_DIR = Path(__file__).parent.expanduser().resolve()
TEMPLATES_DIR = APP_DIR / 'templates'
BASE_DIR = APP_DIR.parent

#######################################
#
# General configuration
#
#######################################
cf['DEBUG'] = DEBUG = env_bool('DEBUG', True if env('FLASK_ENV') == 'development' else False)

HOST = cf['HOST'] = env('HOST', '127.0.0.1')
PORT = cf['PORT'] = env_int('PORT', 5111)

cf['API_ONLY'] = env_bool('API_ONLY', False)
"""If set to ``True``, will always return JSON, never HTML pages."""

USE_IP_HEADER = cf['USE_IP_HEADER'] = env_bool('USE_IP_HEADER', True)
"""If set to False, will obtain the IP from request.remote_addr instead of the header set in IP_HEADER"""
IP_HEADER = cf['IP_HEADER'] = env('IP_HEADER', 'X-REAL-IP')
"""The name of the header that will be passed to Flask containing the IP address of the user"""

USE_FAKE_IPS = env_bool('USE_FAKE_IPS', DEBUG)
"""
USE_FAKE_IPS causes the app to always use FAKE_V4 and FAKE_V6 as the detected client's v4/v6 IPs, which aids
testing the app when running it locally during development.
"""
FAKE_V4 = empty_if(env('FAKE_V4', '185.130.44.140' if USE_FAKE_IPS else ''), None)
FAKE_V6 = empty_if(env('FAKE_V6', '2a07:e01:123::456' if USE_FAKE_IPS else ''), None)

MAX_ADDRESSES = env_int('MAX_ADDRESSES', 20)

MAIN_HOST = env('MAIN_HOST', 'myip.privex.io')

V6_SUBDOMAIN = cf['V6_SUBDOMAIN'] = env('V6_SUBDOMAIN', 'v6')
V4_SUBDOMAIN = cf['V4_SUBDOMAIN'] = env('V4_SUBDOMAIN', 'v4')

# To be able to detect both a user's IPv4 and IPv6 addresses, you must have two subdomains
# with the ipv4 subdomain having only an A record, and the ipv6 subdomain with only an AAAA.
V6_HOST = cf['V6_HOST'] = env('V6_HOST', f'{V6_SUBDOMAIN}.{MAIN_HOST}')
V4_HOST = cf['V4_HOST'] = env('V4_HOST', f'{V4_SUBDOMAIN}.{MAIN_HOST}')

DBG_HOSTS = [
    '127.0.0.1', f'127.0.0.1:{PORT}', '127.0.0.1:5000', '[::1]', '[::1]:5000', f'[::1]:{PORT}',
    'localhost', f'localhost:{PORT}', 'l.privex.io', '*.l.privex.io', f'l.privex.io:{PORT}', f'*.l.privex.io:{PORT}',
    f'l4.privex.io:{PORT}', f'*.l4.privex.io:{PORT}', f'l6.privex.io:{PORT}', f'*.l6.privex.io:{PORT}',
] if DEBUG else []

EXTRA_HOSTS = env_csv('EXTRA_HOSTS', ['myip.vc', 'address.computer'])
"""
Additional domains to use, alongside MAIN_HOST. They'll be added to ALLOWED_HOSTS, including the V4 and V6_SUBDOMAIN
"""

FORCE_MAIN_HOST = env_bool('FORCE_MAIN_HOST', DEBUG)
"""
When this is True, v4/v6_host in the template context will always use settings.V4_HOST and settings.V6_HOST,
instead of reading the host from the headers.
"""

CACHE_ADAPTER = env('CACHE_ADAPTER', None)
"""
This environment var controls which caching adapter is used to store any cached data from the app.

By default, it's set to ``None`` (empty) which results in the ``auto`` behaviour - trying redis, memcached, and then memory cache,
until one works.

Choices:

  * blank ``''`` / auto / automatic = All three of these options result in the variable being set to ``None``, which results in
    the default cache adapter selection behaviour being used, which tries the following 3 cache adapters in order, until one works::

      * ``RedisCache`` - requires the ``redis`` PyPi package to be used
      * ``MemcachedCache`` - requires the ``pylibmc`` PyPi package to be used
      * ``MemoryCache`` - dependency free (apart from the package containing this adapter itself - ``privex-helpers``)

  * ram / mem / memory / MemoryCache = In-Memory cache - stores cached data in application RAM, which is lost when app is restarted.

  * mcache / memcache / memcached / MemcachedCache = Memcached cache - stores cached data in ``memcached`` server,
    requires ``memcached`` service to be installed and running on the server

  * redis / RedisCache / RedisAdapter = Redis cache - stores cached data in ``redis`` server, requires ``redis`` service
    to be installed and running either on this server, or a remote one specified using ``REDIS_HOST`` / ``REDIS_PORT`` env vars.

  * sqlite / sqlite3 / sqlitedb = SQLite3 DB Cache - This is the only persistent cache which doesn't require a separate
    server daemon to be running alongside the app. However, it can have performance issues, as it's file-based design means
    only one thing (thread/app) can write to it at a time, while both reading/writing likely involves Python's GIL,
    preventing asynchronous / threaded code from properly running in parallel.

"""

if empty(CACHE_ADAPTER, zero=True) or CACHE_ADAPTER.lower() in ['auto', 'automatic']:
    CACHE_ADAPTER = None

CACHE_ADAPTER_SET = False

CACHE_ADAPTER_INIT = env_bool('CACHE_ADAPTER_INIT', True)
"""
When true, automatically sets and initialises the cache adapter in core.py during app init. When False,
the cache adapter will be lazy-set/init, i.e. only setup once something calls :func:`myip.core.get_cache`
"""

def _gen_hosts(*domains) -> list:
    domlist = []
    for domain in domains:
        domlist += [f'{domain}', f"{V4_SUBDOMAIN}.{domain}", f"{V6_SUBDOMAIN}.{domain}", f"*.{domain}"]
    return domlist

FILTER_HOSTS = env_bool('FILTER_HOSTS', True)
"""
This controls whether or not we set ``trusted_hosts`` - when trusted_hosts is set, it will cause hosts which aren't
whitelisted in ``ALLOWED_HOSTS`` to trigger a 401 BAD REQUEST error.

You should generally keep this enabled.

If you have problems with the trusted_hosts filtering, or you simply just want to allow the site to work on any
arbitrary domain, then you can set this to false in your .env file.
"""

ALLOWED_HOSTS = env_csv(
    'ALLOWED_HOSTS', [
        MAIN_HOST, f"*.{MAIN_HOST}", V4_HOST, V6_HOST,
    ] + _gen_hosts(*EXTRA_HOSTS) + DBG_HOSTS
)

pvx_settings.GEOIP_DIR = GEOIP_PATH = Path(env('GEOIP_PATH', '/usr/local/var/GeoIP')).expanduser().resolve()
"""An absolute path to the GeoIP2 folder containing mmdb files to use for IP lookups (NO ENDING SLASH)"""
cf['GEOIP_PREFIX'] = GEOIP_PREFIX = env('GEOIP_PREFIX', 'GeoLite2-')
"""The prefix for the GeoIP2 files, generally either 'GeoIP2-' for paid, or 'GeoLite2-' for the free edition"""

pvx_settings.GEOASN_NAME = GEOASN_NAME = f"{GEOIP_PREFIX}ASN.mmdb"
pvx_settings.GEOCITY_NAME = GEOCITY_NAME = f"{GEOIP_PREFIX}City.mmdb"
pvx_settings.GEOCOUNTRY_NAME = GEOCOUNTRY_NAME = f"{GEOIP_PREFIX}Country.mmdb"
pvx_settings.GEOASN = GEOIP_PATH / GEOASN_NAME
pvx_settings.GEOCITY = GEOIP_PATH / GEOCITY_NAME
pvx_settings.GEOCOUNTRY = GEOIP_PATH / GEOCOUNTRY_NAME

cf['GEOIP_CACHE_SEC'] = GEOIP_CACHE_SEC = int(env('GEOIP_CACHE_SEC', 10 * MINUTE))
"""Amount of seconds to cache GeoIP data in Redis for. Default is 600 seconds (10 minutes)"""

cf['RDNS_CACHE_SEC'] = RDNS_CACHE_SEC = int(env('RDNS_CACHE_SEC', 1 * HOUR))
"""Amount of seconds to cache Reverse DNS (rDNS) lookup results. Default is 1 hour (3600 seconds)"""

pvx_settings.REDIS_HOST = REDIS_HOST = env('REDIS_HOST', 'localhost')
pvx_settings.REDIS_PORT = REDIS_PORT = int(env('REDIS_PORT', 6379))
pvx_settings.REDIS_DB = REDIS_DB = int(env('REDIS_DB', 0))

#######################################
#
# Logging configuration
#
#######################################
# Log to console with LOG_LEVEL, as well as output logs >=info / >=warning to respective files
# with automatic daily log rotation (up to 14 days of logs)
# Due to the amount of output from logging.DEBUG, we only log INFO and higher to a file.
# Valid environment log levels (from least to most severe) are:
# DEBUG, INFO, WARNING, ERROR, FATAL, CRITICAL

USE_RICH_LOGGING = env_bool('USE_RICH_LOGGING', True)
RICH_TRACEBACKS = env_bool('RICH_TRACEBACKS', True)

if RichHandler is None: USE_RICH_LOGGING = False

LOG_LEVEL = str(env('LOG_LEVEL', 'DEBUG' if DEBUG else 'WARNING')).upper()
LOG_LEVEL = logging.getLevelName(LOG_LEVEL)
LOG_DIR = Path(env('LOG_DIR', 'logs')).expanduser()
LOG_DIR = BASE_DIR / str(LOG_DIR) if not LOG_DIR.is_absolute() else LOG_DIR.resolve()

if not LOG_DIR.exists():
    LOG_DIR.mkdir(parents=True, exist_ok=True)

DBG_LOG, ERR_LOG = str(LOG_DIR / 'debug.log'), str(LOG_DIR / 'error.log')


import scrapy
import math
import json
import re
import os
from chalaoshi.items import TeacherItem, CommentListItem
from chalaoshi.settings import SAVE_FILE, MAX_INDEX

COUNT_PER_PAGE = 10

class ChalaoshiSpider(scrapy.Spider):
    name = 'chalaoshi'
    allowed_domains = ['chalaoshi.cn']
    start_urls = ['https://chalaoshi.cn/']

    # Record saved teachers and comments
    saved_data = {}

    # Teacher info url
    teacher_url = 'https://chalaoshi.cn/teacher/{index}'
    # Comment info url
    comment_url = 'https://chalaoshi.cn/teacher/{index}/comment_list?page={page}&order_by=rate'

    # Teacher info patterns
    teacher_name_pat = re.compile('
(.*)
')
    rate_pat = re.compile('
(.*)
')
    details_pat = re.compile('
(.*)
')
    comment_count_pat = re.compile('
([\d]*)')

    # Comment patterns
    comment_text_pat = re.compile('
\n\s{16}([\s\S]*?)\n\s{12}
')
    comment_vote_pat = re.compile('
\s*(-?\d*)\s*
')
    comment_time_pat = re.compile('
发布于\s*(\d{4}\.\d{2}\.\d{2})\s*<a href="#" onclick')

    # Handle invalid status
    handle_httpstatus_list = [400, 401, 402, 403, 404, 500, 501, 502]

    # Find next teacher not fetched
    def find_next(self, index):
        while index in self.saved_data:
            index += 1
        return index

    # Calculate priority according to the index
    @staticmethod
    def calc_prior(index):
        return (MAX_INDEX - index) * 100

    # Entry of spider
    def start_requests(self):
        # Start from index last loaded if saved_file exists
        if os.path.exists(SAVE_FILE):
            with open(SAVE_FILE, 'r') as f:
                json_dict = json.load(f)
                # Remember json dict use str as keys, needs transformation back to int
                self.saved_data = {int(k): v for k, v in json_dict.items()}
        else:
            self.saved_data = {}
        # Start to find index from 1
        index = self.find_next(1)
        if index <= MAX_INDEX:
            yield scrapy.Request(self.teacher_url.format(index=index), callback=self.parse,
                                 priority=self.calc_prior(index), meta={'index': index})
        else:
            print('All teachers within {} has been collected:)'.format(MAX_INDEX))

    # Parse teacher information
    def parse(self, response):
        index = response.meta['index']
        code = response.status
        # Check status code
        if code in self.handle_httpstatus_list:
            self.saved_data[index] = {'name': 'Not applicable', 'comment_count': 0}
            # Invalid teacher index
            if 400 <= code < 500:
                print('Invalid teacher index {}, or network failed.'.format(index))
                index = self.find_next(index + 1)
                if index <= MAX_INDEX:
                    yield scrapy.Request(self.teacher_url.format(index=index), callback=self.parse,
                                         priority=self.calc_prior(index), meta={'index': index})
                else:
                    print('All teachers within {} has been collected:)'.format(MAX_INDEX))
            # Server not available
            elif code >= 500:
                print('Internal server error: chalaoshi.cn is not available now.')
            return
        content = response.text
        item = TeacherItem()
        try:
            # Basic information, use splice to remove `<...>`s
            item['index'] = index
            item['name'] = self.teacher_name_pat.search(content).group()[4:-5]
            item['rate_avg'] = self.rate_pat.search(content).group()[4:-5]
            # Details: strings between `<p>` and `</p>`
            details = self.details_pat.findall(content)
            item['college'] = details[0]
            item['department'] = details[1]
            # Parse other details
            course_index = 4
            if details[2] == '尚未收到足够的评分':
                item['rate_call_roll'] = 'N/A'
                item['rate_count'] = 0
                course_index = 3
            else:
                item['rate_call_roll'] = details[2][:details[2].find('%') + 1]
                item['rate_count'] = int(details[3][:details[3].find('人参与评分')])
            # Parse courses
            item['course_list'] = []
            while course_index < len(details):
                course = {'name': details[course_index], 'gpa_avg': details[course_index + 1].split('/')[0],
                          'gpa_count': details[course_index + 1].split('/')[1]}
                item['course_list'].append(course)
                course_index += 2
            # Parse comment counts
            count = self.comment_count_pat.search(content)
            if count is None:
                item['comment_count'] = 0
            else:
                item['comment_count'] = int(count.group()[15:])
            # Give item to pipeline
            yield item
            # Get comments
            if item['comment_count'] > 0:
                comments = CommentListItem()
                comments['name'] = item['name']
                comments['comment_count'] = item['comment_count']
                comments['comment_list'] = []
                # Page index starts from 0
                page_total = int(math.ceil(item['comment_count'] / COUNT_PER_PAGE)) - 1
                yield scrapy.Request(self.comment_url.format(index=index, page=0), callback=self.parse_comments,
                                     meta={'page_index': 0, 'item': comments, 'page_total': page_total,
                                           'index': index}, priority=self.calc_prior(index))
            else:
                self.saved_data[index] = {'name': item['name'], 'comment_count': item['comment_count']}
            print('Fetched teacher {} named {}.'.format(index, item['name']))
        # Something went wrong for my regex expression or teacher information
        except AttributeError as e:
            print('Error parsing teacher with index: {}: {}'.format(index, e))
        finally:
            # Fetch next teacher recursively
            # Start from index + 1 since current index is not done yet:
            # as comments are not parsed, the current index will not be in the saved dict
            index = self.find_next(index + 1)
            if index <= MAX_INDEX:
                yield scrapy.Request(self.teacher_url.format(index=index), callback=self.parse,
                                     priority=self.calc_prior(index), meta={'index': index})
            else:
                print('All teachers within {} has been collected:)'.format(MAX_INDEX))

    # Parse one page each containing at most 10 comments
    def parse_comments(self, response):
        # Recover param
        item = response.meta['item']
        page_index = response.meta['page_index']
        page_total = response.meta['page_total']
        index = response.meta['index']
        # Parse contents
        content = response.text.replace(' ', ' ')
        texts = self.comment_text_pat.findall(content)
        votes = self.comment_vote_pat.findall(content)
        times = self.comment_time_pat.findall(content)
        # This means something went wrong for my regex expression
        # Or the content of comments
        try:
            for i in range(len(texts)):
                comment = {'text': texts[i], 'vote': int(votes[i]), 'time': times[i]}
                item['comment_list'].append(comment)
        except AttributeError as e:
            print('Error parsing comments with teacher index {} at page {}: {}'.format(index, item.page_index, e))
            print(content)
        finally:
            page_index += 1
            if page_index <= page_total:
                yield scrapy.Request(self.comment_url.format(index=index, page=page_index),
                                     callback=self.parse_comments,
                                     meta={'page_index': page_index, 'item': item, 'page_total': page_total,
                                           'index': index}, priority=self.calc_prior(index))
            if item['comment_count'] == len(item['comment_list']):
                print('Fetched {} comments from teacher {}.'.format(item['comment_count'], item['name']))
                self.saved_data[index] = {'name': item['name'], 'comment_count': item['comment_count']}
                yield item

    @staticmethod
    def close(spider, reason):
        # Overwrite saved data records
        with open(SAVE_FILE, 'w') as f:
            # json will transform int keys into string keys
            json.dump(spider.saved_data, f, ensure_ascii=False, separators=(',', ': '), indent=4)


import json

from . import graph

def label(vertex):
    """Graph vertex label in dot format"""
    label = f"{vertex.name} {vertex.state or ''}\n{vertex.traceback or ''}"
    if not label.endswith("\n"):
        label += "\n"
    label = json.dumps(label).replace("\\n", r"\l")
    return f"[label={label}]"

def dumps(tasks):
    """
    Renders Task dependency graph in graphviz format.
    Returns a string.
    """

    prefix = "\n        "

    vertices, edges = graph.new(tasks)
    vertices = prefix.join(f"{id(vertex.task)} {label(vertex)}" for vertex in vertices)
    edges = prefix.join(f"{id(edge.src.task)} -> {id(edge.dst.task)}" for edge in edges)

    return f"""
    digraph {{
        node [shape="note", fontname="Courier New"];
        {vertices}
        {edges}
    }}
    """.strip()


import pytest
from src.realty.landwatch import Query
from src.realty.landwatch.details import Listing_Details

@pytest.fixture(scope="module")
def reasonable_query():
    q = Query()
    return q.get_response()

@pytest.fixture(scope="module")
def reasonable_results():
    q = Query()
    return q.get_results()['propertyResults']

class TestQuery:

    @staticmethod
    def test_status_code(reasonable_query):
        assert reasonable_query.status_code == 200

    @staticmethod
    def test_expected_json_keys(reasonable_query):
        assert list(
            reasonable_query.json().keys()
        ) == ['activeFilters', 'amChartMapData', 'breadCrumbSchema', 'brokerDetails', 'carouselCounts', 'collectionPageSchema', 'dataLayerSearchResponse', 'filterSections', 'footer', 'headlineAd', 'routeContext', 'searchResults', 'searchUI', 'seoLinkSections', 'seoTextSection', 'seoTextSection2', 'siteId']

    @staticmethod
    def test_expected_results(reasonable_query):
        assert isinstance(
            reasonable_query.json(
            )['searchResults']['propertyResults'],
            list
        )

    @staticmethod
    def test_more_than_zero_results(reasonable_query):
        assert len(reasonable_query.json(
        )['searchResults']['propertyResults']) > 0

    @staticmethod
    def test_expected_result_keys(reasonable_query):
        # just required ones
        expected_keys = {'accountId', 'acres', 'acresDisplay', 'adTargetingCountyId', 'address', 'auctionDate', 'baths', 'bathsDisplay', 'bedsDisplay', 'beds', 'brokerCompany', 'brokerName', 'canonicalUrl', 'city', 'cityID', 'companyLogoDocumentId', 'county', 'countyId', 'countyLabel', 'description', 'encodedBoundaryPoints', 'externalSourceId', 'halfBaths', 'halfBathsDisplay', 'hasHouse', 'hasVideo', 'hasVirtualTour', 'homesqft', 'homesqftDisplay', 'imageCount', 'imageAltTextDisplay',
                         'id', 'isALC', 'isDiamond', 'isFirstFreeListing', 'isGold', 'isHeadlineAd', 'isLiked', 'isPlatinum', 'isShowcase', 'lake', 'latitude', 'listHubListingKey', 'listingLevel', 'listingLevelTitle', 'longitude', 'partnerId', 'portraitDocumentId', 'price', 'priceChange', 'priceDisplay', 'propertyTypes', 'propertyTypesLabel', 'schemaData', 'shortPrice', 'siteListingId', 'state', 'stateAbbreviation', 'stateCode', 'stateId', 'status', 'thumbnailDocumentId', 'title', 'types', 'zip'}

        results = reasonable_query.json(
        )['searchResults']['propertyResults']

        assert all([
            expected_keys - set(r.keys()) == set() for r in results
        ])

class TestListingDetails:
    @staticmethod
    @pytest.fixture(scope="class")
    def listing_subset(reasonable_results):
        return [
            Listing_Details(lr) for lr in reasonable_results[:3]
        ]

    @staticmethod
    def test_init(listing_subset):
        # just a basic check if init doesn't run into errors
        listing_subset


import logging
from contextlib import ExitStack
from tempfile import TemporaryDirectory
from typing import Optional

from requests import Session
from todoist.api import SyncError, TodoistAPI

from tools_for_todoist.models.item import TodoistItem
from tools_for_todoist.storage import get_storage
from tools_for_todoist.utils import retry_flaky_function

logger = logging.getLogger(__name__)

TODOIST_API_KEY = 'todoist.api_key'

class Todoist:
    def __init__(self):
        self._exit_stack: Optional[ExitStack] = None
        self._recreate_api()
        self.api.reset_state()
        self._items = {}
        self._projects = {}
        self._last_completed = None
        self._initial_sync()

    def _recreate_api(self):
        if self._exit_stack is not None:
            self._exit_stack.close()
        self._exit_stack = ExitStack()
        token = get_storage().get_value(TODOIST_API_KEY)
        headered_session = Session()
        headered_session.headers['Authorization'] = f'Bearer {token}'

        new_temp_dir = self._exit_stack.enter_context(TemporaryDirectory())
        self.api = TodoistAPI(token, session=headered_session, api_version='v9', cache=new_temp_dir)

    def _activity_sync(self, offset=0, limit=100):
        def activity_get_func():
            return self.api.activity.get(
                object_type='item',
                event_type='completed',
                offset=offset,
                limit=limit,
            )

        return retry_flaky_function(
            activity_get_func,
            'todoist_activity_get',
            validate_result_func=lambda x: x and 'count' in x and 'events' in x,
            on_failure_func=self._recreate_api,
        )

    def _update_projects(self, sync_result):
        for project in sync_result['projects']:
            self._projects[project['id']] = project

    def _initial_sync(self):
        self._initial_result = retry_flaky_function(
            self.api.sync,
            'todoist_initial_sync',
            on_failure_func=self._recreate_api,
            validate_result_func=lambda x: x and 'projects' in x and 'items' in x,
        )
        for item in self._initial_result['items']:
            self._items[item['id']] = TodoistItem.from_raw(self, item)
        self._update_projects(self._initial_result)
        for project_id in self._projects.keys():
            # TODO(kris): Improve this completed logic or deprecate
            for item in self.api.items.get_completed(project_id, limit=200):
                self._items[item['id']] = TodoistItem.from_raw(self, item)
        activity_result = self._activity_sync(limit=1)
        if activity_result['count']:
            self._last_completed = activity_result['events'][0]['id']

    def _new_completed(self):
        finished_processing = False
        offset = 0
        first_event = None
        new_completed = set()

        while not finished_processing:
            activity_result = self._activity_sync(offset=offset)
            offset += 100
            finished_processing = offset > activity_result['count']
            for event in activity_result['events']:
                if first_event is None:
                    first_event = event['id']
                if event['id'] == self._last_completed:
                    finished_processing = True
                    break
                new_completed.add(event['object_id'])
        self._last_completed = first_event
        return new_completed

    def _update_items(self, raw_updated_items):
        deleted_items = []
        new_items = []
        updated_items = []
        for item in raw_updated_items:
            if item['is_deleted'] == 1:
                old_item = self._items.pop(item['id'], None)
                if old_item is not None:
                    deleted_items.append(old_item)
            elif item['id'] not in self._items:
                new_item = TodoistItem.from_raw(self, item)
                self._items[new_item.id] = new_item
                new_items.append(new_item)
            else:
                old_item = TodoistItem.from_raw(self, self._items[item['id']].raw())
                item_model = self.get_item_by_id(item['id'])
                item_model.update_from_raw(item)
                updated_items.append((old_item, item_model))
        return {'deleted': deleted_items, 'created': new_items, 'updated': updated_items}

    def get_item_by_id(self, item_id: str) -> TodoistItem:
        return self._items.get(item_id)

    def get_project_by_name(self, name):
        for project in self._projects.values():
            if project['name'] == name:
                return project
        return None

    def create_label(self, name):
        logger.info(f'Creating label| {name}')
        return self.api.labels.add(name)['id']

    def add_item(self, item):
        logger.info(f'Adding item| {item}')
        item_raw = self.api.items.add(
            item.content,
            project_id=item.project_id,
            priority=item.priority,
            due=item._due,
            labels=list(item.labels()),
        )
        self._items[item_raw['id']] = item
        return item_raw.data

    def update_item(self, item, **kwargs):
        logger.info(f'Updating item| {item}')
        self.api.items.update(item.id, **kwargs)

    def delete_item(self, item):
        logger.info(f'Deleting item| {item}')
        self.api.items.delete(item.id)

    def archive_item(self, item):
        logger.info(f'Archiving item| {item}')
        self.api.items.complete(item.id)

    def uncomplete_item(self, item):
        logger.info(f'Uncompleting item| {item}')
        self.api.items.uncomplete(item.id)

    def sync(self):
        api_queue = self.api.queue

        def api_sync():
            if len(api_queue) > 0:
                self.api.queue = api_queue.copy()
                return self.api.commit()
            return self.api.sync()

        result = retry_flaky_function(
            api_sync,
            'todoist_api_sync',
            on_failure_func=self._recreate_api,
            validate_result_func=lambda x: x and 'items' in x,
            critical_errors=[SyncError],
        )
        try:
            for temporary_key, new_id in result.get('temp_id_mapping', {}).items():
                item = self._items.pop(temporary_key, None)
                if item:
                    item.id = new_id
                    self._items[new_id] = item
            item_updates = [x for x in result['items']]
            self._update_projects(result)
            sync_result = self._update_items(item_updates)
        except Exception as e:
            logger.exception(f'Todoist Sync Failed| {result}', exc_info=e)
            raise
        sync_result['raw'] = result
        sync_result['completed'] = self._new_completed()
        return sync_result


import numpy as np
import torch

from saliency_metrics.datasets import build_pipeline

def test_build_pipeline():
    cfg = [
        dict(type="Resize", height=5, width=5),
        dict(type="Normalize", mean=(0.2, 0.3, 0.4), std=(0.2, 0.3, 0.4)),
        dict(type="ToTensorV2"),
    ]
    pipeline = build_pipeline(cfg)
    img = np.ones((8, 8, 3)) * np.array([120, 140, 160])
    img = img.astype(np.uint8)
    img_transformed = pipeline(image=img)["image"]
    expected_values = (torch.tensor([120, 140, 160]) - torch.tensor([0.2, 0.3, 0.4]) * 255) / (
        torch.tensor([0.2, 0.3, 0.4]) * 255
    )
    expected = torch.ones((3, 5, 5), dtype=torch.float32) * expected_values.reshape(3, 1, 1)
    torch.testing.assert_allclose(img_transformed, expected)


import sys
sys.stdin = open('input.txt','r')

n = int(input())
traveler = list(map(int,input().split()))
cnt = 0
res = 0
for i in traveler:
    cnt += 1
    if cnt>= i:
        res+=1
        cnt = 0
print(res)


import abc
import functools

import numpy as np
import scipy.integrate

from eFFORT.utility import BGL_form_factor, z_var, PDG

class BToDstarLNu:
    """
    A class containing functions specific to the differential decay rate of the B to Dstar transitions with the BCL/BGL
    parametrization. If not states otherwise, the numerical values and variable/function definitions are taken from:
    https://arxiv.org/abs/1702.01521v2 and ...

    Zero mass approximation for the lepton is implicit.
    The Blaschke factors do not explicitly appear, because they are 1.
    """

    def __init__(self, m_B: float, m_Dstar: float, V_cb: float, eta_EW: float = 1.0066) -> None:
        # Some of the following can be inherited from a parent class / initialized from a super constructor in the
        # future.
        self.m_B = m_B
        self.m_Dstar = m_Dstar
        self.V_cb = V_cb
        self.eta_EW = eta_EW
        self.G_F = PDG.G_F

        self.w_min = 1
        self.w_max = (m_B ** 2 + m_Dstar ** 2) / (2 * m_B * m_Dstar)

        # Variables which are often used and can be computed once
        self.r = self.m_Dstar / self.m_B
        self.rprime = 2 * np.sqrt(self.m_B * self.m_Dstar) / (self.m_B + self.m_Dstar)

        self._gamma_int = {
            22: None,
            111: None,
            211: None,
        }

    def A0(self, w):
        raise RuntimeError("Not implemented. But also not required for light leptons.")

    def A1(self, w):
        return (w + 1) / 2 * self.rprime * self.h_A1(w)

    def A2(self, w):
        return self.R2(w) / self.rprime * self.h_A1(w)

    def V(self, w):
        return self.R1(w) / self.rprime * self.h_A1(w)

    def Hplus(self, w):
        return (self.m_B + self.m_Dstar) * self.A1(w) - 2 * self.m_B / (self.m_B + self.m_Dstar) * self.m_Dstar * (
                w ** 2 - 1) ** 0.5 * self.V(w)

    def Hminus(self, w):
        return (self.m_B + self.m_Dstar) * self.A1(w) + 2 * self.m_B / (self.m_B + self.m_Dstar) * self.m_Dstar * (
                w ** 2 - 1) ** 0.5 * self.V(w)

    def Hzero(self, w):
        m_B = self.m_B
        m_D = self.m_Dstar
        q2 = (m_B ** 2 + m_D ** 2 - 2 * w * m_B * m_D)
        return 1 / (2 * m_D * q2 ** 0.5) * ((m_B ** 2 - m_D ** 2 - q2) * (m_B + m_D) * self.A1(w)
                                            - 4 * m_B ** 2 * m_D ** 2 * (w ** 2 - 1) / (m_B + m_D) * self.A2(w))

    @abc.abstractmethod
    def h_A1(self, w: float) -> float:
        pass

    @abc.abstractmethod
    def R0(self, w: float) -> float:
        raise RuntimeError("Not implemented. But also not required for light leptons.")

    @abc.abstractmethod
    def R1(self, w: float) -> float:
        pass

    @abc.abstractmethod
    def R2(self, w: float) -> float:
        pass

    def dGamma_dw_dcosL_dcosV_dChi(self, w, cos_l, cos_nu, chi, pdg):
        return np.where(
            np.abs(pdg) == 22,
            self.dGamma_dw_dcosL_dcosV_dChi_gamma(w, cos_l, cos_nu, chi),
            self.dGamma_dw_dcosL_dcosV_dChi_pion(w, cos_l, cos_nu, chi)
        )

    def dGamma_dw_dcosL_dcosV_dChi_pion(self, w, cos_l, cos_nu, chi):
        sin_l = (1 - cos_l ** 2) ** 0.5
        sin_nu = (1 - cos_nu ** 2) ** 0.5

        Hplus = self.Hplus(w)
        Hminus = self.Hminus(w)
        Hzero = self.Hzero(w)

        return 6 * self.m_B * self.m_Dstar ** 2 / 8 / (4 * np.pi) ** 4 * (w ** 2 - 1) ** 0.5 * (
                1 - 2 * w * self.r + self.r ** 2) * self.G_F ** 2 * self.V_cb ** 2 * (
                       (1 - cos_l) ** 2 * sin_nu ** 2 * Hplus ** 2
                       + (1 + cos_l) ** 2 * sin_nu ** 2 * Hminus ** 2
                       + 4 * sin_l ** 2 * cos_nu ** 2 * Hzero ** 2
                       - 2 * sin_l ** 2 * sin_nu ** 2 * np.cos(2 * chi) * Hplus * Hminus
                       - 4 * sin_l * (1 - cos_l) * sin_nu * cos_nu * np.cos(chi) * Hplus * Hzero
                       + 4 * sin_l * (1 + cos_l) * sin_nu * cos_nu * np.cos(chi) * Hminus * Hzero
               )

    def dGamma_dw_dcosL_dcosV_dChi_gamma(self, w, cos_l, cos_nu, chi):
        sin_l = (1 - cos_l ** 2) ** 0.5
        sin_nu = (1 - cos_nu ** 2) ** 0.5

        Hplus = self.Hplus(w)
        Hminus = self.Hminus(w)
        Hzero = self.Hzero(w)

        return 6 * self.m_B * self.m_Dstar ** 2 / 8 / (4 * np.pi) ** 4 * (w ** 2 - 1) ** 0.5 * (
                1 - 2 * w * self.r + self.r ** 2) * self.G_F ** 2 * self.V_cb ** 2 * (
                       (1 - cos_l) ** 2 * (1 + cos_nu ** 2) * Hplus ** 2
                       + (1 + cos_l) ** 2 * (1 + cos_nu ** 2) * Hminus ** 2
                       + 4 * sin_l ** 2 * sin_nu ** 2 * Hzero ** 2
                       - 2 * sin_l ** 2 * (-1) * sin_nu ** 2 * np.cos(2 * chi) * Hplus * Hminus
                       - 4 * sin_l * (1 - cos_l) * (-1) * sin_nu * cos_nu * np.cos(chi) * Hplus * Hzero
                       + 4 * sin_l * (1 + cos_l) * (-1) * sin_nu * cos_nu * np.cos(chi) * Hminus * Hzero
               )

    def dGamma_dw(self, w, pdg):
        return scipy.integrate.nquad(
            lambda cosL, cosV, chi: self.dGamma_dw_dcosL_dcosV_dChi(w, cosL, cosV, chi, pdg),
            [[-1, 1], [-1, 1], [0, 2 * np.pi]]
        )[0]

    def dGamma_dcosLepton(self, cosl, pdg):
        return scipy.integrate.nquad(
            lambda w, cosV, chi: self.dGamma_dw_dcosL_dcosV_dChi(w, cosl, cosV, chi, pdg),
            [[self.w_min, self.w_max], [-1, 1], [0, 2 * np.pi]]
        )[0]

    def dGamma_dcosNeutrino(self, cosnu, pdg):
        return scipy.integrate.nquad(
            lambda w, cosL, chi: self.dGamma_dw_dcosL_dcosV_dChi(w, cosL, cosnu, chi, pdg),
            [[self.w_min, self.w_max], [-1, 1], [0, 2 * np.pi]]
        )[0]

    def dGamma_dchi(self, chi, pdg):
        return scipy.integrate.nquad(
            lambda w, cosL, cosV: self.dGamma_dw_dcosL_dcosV_dChi(w, cosL, cosV, chi, pdg),
            [[self.w_min, self.w_max], [-1, 1], [-1, 1]]
        )[0]

    def Gamma(self, pdg):
        gamma = np.full(len(pdg), np.nan)
        for pdg_code in self._gamma_int.keys():
            gamma[abs(pdg) == pdg_code] = self._gamma_int[pdg_code]
        assert not np.isnan(gamma).any()
        return gamma

    def _Gamma(self, pdg):
        w_min = 1
        w_max = (self.m_B ** 2 + self.m_Dstar ** 2) / (2 * self.m_B * self.m_Dstar)
        return scipy.integrate.nquad(
            self.dGamma_dw_dcosL_dcosV_dChi,
            [[w_min, w_max], [-1, 1], [-1, 1], [0, 2 * np.pi]],
            args=(pdg,)
        )[0]

    def get_gammas(self):
        return self._gamma_int

    @staticmethod
    def check_precomputed_gammas_dict(gammas_dict):
        if not isinstance(gammas_dict, dict):
            raise ValueError(
                f"The parameter cached_gammas must be a dictionary containing precomputed integral values.\n"
                f"The provided cached_gammas was of the type {type(gammas_dict)}."
            )
        if not len(gammas_dict) == 3 or not all(k in gammas_dict.keys() for k in [22, 111, 211]):
            raise KeyError(
                f"The provided cached_gammas dictionary must contain values for the three keys 22, 111 and 211.\n"
                f"It contained the {len(gammas_dict)} keys {list(gammas_dict.keys())}."
            )
        if not all(isinstance(v, float) for v in gammas_dict.values()):
            raise ValueError(f"The provided cached_gamas dictionary must contain floats as values.")

class BToDstarLNuCLN(BToDstarLNu):

    def __init__(self, m_B: float, m_Dstar: float, V_cb: float, eta_EW: float = 1.0066, cached_gammas=None):
        super().__init__(m_B, m_Dstar, V_cb, eta_EW)

        # CLN specifics, default is given by values in https://arxiv.org/abs/1702.01521v2
        self.h_A1_1 = 0.906
        self.rho2 = 1.03
        self.R1_1 = 1.38
        self.R2_1 = 0.97

        if cached_gammas is None:
            self._gamma_int[22] = self._Gamma(22)
            self._gamma_int[111] = self._Gamma(111)
            self._gamma_int[211] = self._gamma_int[111]
        else:
            self.check_precomputed_gammas_dict(cached_gammas)
            self._gamma_int = cached_gammas

    def h_A1(self, w):
        rho2 = self.rho2
        _z = z_var(w)
        return self.h_A1_1 * (1 - 8 * rho2 * _z + (53 * rho2 - 15) * _z ** 2 - (231 * rho2 - 91) * _z ** 3)

    def R1(self, w):
        return self.R1_1 - 0.12 * (w - 1) + 0.05 * (w - 1) ** 2

    def R2(self, w):
        return self.R2_1 + 0.11 * (w - 1) - 0.06 * (w - 1) ** 2

class BToDstarLNuBelle(BToDstarLNu):

    def __init__(self, m_B: float, m_Dstar: float, V_cb: float, eta_EW: float = 1.0066):
        super(BToDstarLNuBelle, self).__init__(m_B, m_Dstar, V_cb, eta_EW)

        # CLN specifics, default is given by values in https://arxiv.org/abs/1702.01521v2
        self.h_A1_1 = 0.906
        self.rho2 = 1.3
        self.R1_1 = 1.18
        self.R2_1 = 0.71

    def h_A1(self, w):
        rho2 = self.rho2
        _z = z_var(w)
        return self.h_A1_1 * (1 - 8 * rho2 * _z + (53 * rho2 - 15) * _z ** 2 - (231 * rho2 - 91) * _z ** 3)

    def R1(self, w):
        return self.R1_1 - 0.12 * (w - 1) + 0.05 * (w - 1) ** 2

    def R2(self, w):
        return self.R2_1 + 0.11 * (w - 1) - 0.06 * (w - 1) ** 2

class BToDstarLNuBGL(BToDstarLNu):

    def __init__(self, m_B: float, m_Dstar: float, V_cb: float, eta_EW: float = 1.0066, cached_gammas=None,
                 exp_coeff=(3.79139e-04, 2.69537e-02, 5.49846e-04, -2.04028e-03, -4.32818e-04, 5.35029e-03)):
        super().__init__(m_B, m_Dstar, V_cb, eta_EW)

        # BGL specifics, default is given in arXiv:1703.08170v2
        self.chiT_plus33 = 5.28e-4  # GeV^-2
        self.chiT_minus33 = 3.07e-4  # GeV^-2
        self.n_i = 2.6  # effective number of light quarks
        self.axialvector_poles = [6.730, 6.736, 7.135, 7.142]
        self.vector_poles = [6.337, 6.899, 7.012, 7.280]
        # Coefficients from Florian
        self.eta_ew_Vcb = self.eta_EW * self.V_cb
        self.expansion_coefficients_a = [exp_coeff[0] / self.eta_ew_Vcb, exp_coeff[1] / self.eta_ew_Vcb]  # FF g
        self.expansion_coefficients_b = [exp_coeff[2] / self.eta_ew_Vcb, exp_coeff[3] / self.eta_ew_Vcb]  # FF f
        self.expansion_coefficients_c = [
            ((self.m_B - self.m_Dstar) * self.phi_F1(0) / self.phi_f(0)) * self.expansion_coefficients_b[0],
            exp_coeff[4] / self.eta_ew_Vcb, exp_coeff[5] / self.eta_ew_Vcb]  # FF F1

        assert sum([a ** 2 for a in self.expansion_coefficients_a]) <= 1, "Unitarity bound violated."
        assert sum([b ** 2 + c ** 2 for b, c in zip(self.expansion_coefficients_b,
                                                    self.expansion_coefficients_c)]) <= 1, "Unitarity bound violated."
        if cached_gammas is None:
            self._gamma_int[22] = self._Gamma(22)
            self._gamma_int[111] = self._Gamma(111)
            self._gamma_int[211] = self._gamma_int[111]
        else:
            self.check_precomputed_gammas_dict(cached_gammas)
            self._gamma_int = cached_gammas

    def h_A1(self, w):
        z = z_var(w)
        return self.f(z) / (self.m_B * self.m_Dstar) ** 0.5 / (1 + w)

    def R1(self, w):
        z = z_var(w)
        return (w + 1) * self.m_B * self.m_Dstar * self.g(z) / self.f(z)

    def R2(self, w):
        z = z_var(w)
        return (w - self.r) / (w - 1) - self.F1(z) / self.m_B / (w - 1) / self.f(z)

    def g(self, z):
        return BGL_form_factor(z, lambda x: self.blaschke_factor(x, self.vector_poles), self.phi_g,
                               self.expansion_coefficients_a)

    def f(self, z):
        return BGL_form_factor(z, lambda x: self.blaschke_factor(x, self.axialvector_poles), self.phi_f,
                               self.expansion_coefficients_b)

    def F1(self, z):
        return BGL_form_factor(z, lambda x: self.blaschke_factor(x, self.axialvector_poles), self.phi_F1,
                               self.expansion_coefficients_c)

    def blaschke_factor(self, z, poles):
        return np.multiply.reduce([(z - self.z_p(m_pole)) / (1 - z * self.z_p(m_pole)) for m_pole in poles])

    @functools.lru_cache(2 ** 10)
    def z_p(self, m_pole):
        m_B = self.m_B
        m_D = self.m_Dstar
        term1 = ((m_B + m_D) ** 2 - m_pole ** 2) ** 0.5
        term2 = ((m_B + m_D) ** 2 - (m_B - m_D) ** 2) ** 0.5
        return (term1 - term2) / (term1 + term2)

    def phi_g(self, z):
        r = self.r
        return (256 * self.n_i / 3 / np.pi / self.chiT_plus33) ** 0.5 \
               * r ** 2 * (1 + z) ** 2 * (1 - z) ** -0.5 / ((1 + r) * (1 - z) + 2 * r ** 0.5 * (1 + z)) ** 4

    def phi_f(self, z):
        r = self.r
        return 1 / self.m_B ** 2 * (16 * self.n_i / 3 / np.pi / self.chiT_minus33) ** 0.5 \
               * r * (1 + z) * (1 - z) ** (3. / 2) / ((1 + r) * (1 - z) + 2 * r ** 0.5 * (1 + z)) ** 4

    def phi_F1(self, z):
        r = self.r
        return 1 / self.m_B ** 3 * (8 * self.n_i / 3 / np.pi / self.chiT_minus33) ** 0.5 \
               * r * (1 + z) * (1 - z) ** (5. / 2) / ((1 + r) * (1 - z) + 2 * r ** 0.5 * (1 + z)) ** 5

if __name__ == '__main__':
    bToDstar_CLN = BToDstarLNuCLN(PDG.m_Bplus, PDG.m_Dstarzero, 37.4e-3)
    bToDstar_BGL = BToDstarLNuBGL(PDG.m_Bplus, PDG.m_Dstarzero, 41.6558e-3)

    w_min = 1
    w_max = (bToDstar_CLN.m_B ** 2 + bToDstar_CLN.m_Dstar ** 2) / (2 * bToDstar_CLN.m_B * bToDstar_CLN.m_Dstar)

    w_range = np.linspace(w_min + 1e-7, w_max - 1e-7, endpoint=True)
    cosl_range = np.linspace(-1, 1, endpoint=True)
    cosnu_range = np.linspace(-1, 1, endpoint=True)
    chi_range = np.linspace(0, 2 * np.pi, endpoint=True)
    pdg_codes = np.random.choice([22, 111, 211], len(w_range))

    # Example call with numpy arrays
    print(bToDstar_BGL.dGamma_dw_dcosL_dcosV_dChi(w_range, cosl_range, cosnu_range, chi_range, pdg_codes))

    # print("CLN total rate: {}".format(bToDstar_CLN.Gamma()))
    # print("BGL total rate: {}".format(bToDstar_BGL.Gamma()))


'''
Module where the functions that support the update function are written
'''

# '''
# Enabling execution as __main__
# '''
# import os
# os.environ["GIT_PYTHON_REFRESH"] = "quiet"
# import git
# repo = git.Repo('.', search_parent_directories=True)
# os.chdir(repo.working_tree_dir)
# sys.path.append(repo.working_tree_dir)
# print(repo.working_tree_dir)

'''
Importing modules and libraries
'''
import os
os.environ["GIT_PYTHON_REFRESH"] = "quiet"
import numpy as np
import pandas as pd
from datetime import datetime
import time
import SMARTControl.api as api
import SMARTControl.utils as utils
from SMARTControl.api import PegelAlarm
from SMARTControl.api import Inowas
import SMARTControl.queries
import git
from github import Github

fn = 'Data/LOG_UPDATE.txt'

class GWL (Inowas):
    '''
    Inheriting the methods and attributes from api.INOWAS to apply update function
    '''

# Class for INOWAS is too complicated. it's better to use functions.
def Update (Process_df , Get_ ):

    if Process_df is not None:
        Process_df.to_sql(name="PointsMeasurements", con= Get_.connection, if_exists="append", index=False)
        print (f'Diver data updated from ID {Process_df.iloc[0,0]} to ID {Process_df.iloc[-1,0]}')
    else:
        print ('Diver data is up-to-date')

def SequenceUpdate (sensor, sts , Get_):
    with open(fn, 'a+') as f:
        t0 = time.perf_counter()

        ets = round(pd.to_datetime(datetime.now()).value / 1e9)
        SensorsAPI_df, Sensors_df = api.GetDivers(Get_.connection)
        variables =  Sensors_df [ Sensors_df.Diver == sensor].VariableName #indexing variables of interest

        #build on top of the request package and calculate the parameters to request multiple times
        for p in variables:
            t0 = time.perf_counter()

            r = GWL(Get_, sensor, p, sts, ets)
            r.Request()

            Process_df = utils.Process(r.Request_df , Get_)

            if Process_df is None:
                t1 = time.perf_counter()
                last_update = pd.to_datetime(sts * 1e9) - pd.Timedelta(1, unit="h")
                txt = f'\nNo data for sensor {sensor} and parameter {p}. Last update was: {last_update}. Check the portal of UIT. Running time was in {round(t1-t0)} s'
                print (txt)
                f.write (txt)
            else:

                try:

                    '''

                    MISTAKE IN THE UPDATE FUNCTION

                    Fix and Merge INOWAS here
                    '''

                    Update(Process_df, Get_)
                    t1 = time.perf_counter()
                    txt = f'\nDiver data updated from ID {Process_df.iloc[0,0]} to ID {Process_df.iloc[-1,0]} for sensor {sensor} parameter {p} in {round(t1-t0)} s'
                    print (txt)
                    f.write (txt)

                except Exception:
                    txt = f"\nFor a reason it didn't run for sensor {sensor} and parameter {p}. It could be that it is updated "
                    print (txt)
                    f.write (txt)
        txt = f"\n End of Update for sensor {sensor}\n\n"
        print (txt)
        f.write (txt)

def InowasLongAPItoSQL (Get_):

    t0 = time.perf_counter()
    DiversNextUpdate_df = Get_.DiverStatus()
    FunctioningDivers_df = DiversNextUpdate_df  [(DiversNextUpdate_df .IOT == 1) & (DiversNextUpdate_df.Functioning ==1)].reset_index (drop = True)

    if fn in os.listdir():
        with open(fn, 'a+') as f:
            f.write('\n\n')
    with open(fn, 'a+') as f:
        f.write('*************************************NEW RUN INOWAS*************************************')
        now = datetime.now()
        txt = f'\n\nProgram run on the following date: {now}'
        print(txt)
        f.write(txt)

    for i in (FunctioningDivers_df.iterrows()):
        row = i[1]
        sensor, sts = row.DiverName, row.NextUpdate_ts
        if np.isnan(sts):
            sts = 1420934400

        SequenceUpdate (sensor, sts , Get_)

    t1 = time.perf_counter()
    with open(fn, 'a+') as f:
        txt = "\n#####################################END INOWAS######################################"
        print (txt)
        f.write(txt)
        txt = f"\nThe TOTAL for Diver's update running time was {round(t1-t0)} s"
        print (txt)
        f.write(txt)

class RL (PegelAlarm):
    '''
    Inheriting the methods and attributes from api.PagelAlarm to apply update function
    '''
    def Update (self):

        if self.Process_df.shape[0]>0:
            self.Process_df.to_sql(name="PointsMeasurements", con= self.connection, if_exists="append", index=False)
            text = f'\n\nRiver data updated from ID {self.Process_df.iloc[0,0]} to ID {self.Process_df.iloc[-1,0]}'
            return text
        else:
            text = '\n\nRiver data is up-to-date'
            return text

    def RiverAPItoSQL (self):
        if fn in os.listdir():
            with open(fn, 'a+') as f:
                f.write('\n\n\n\n\n\n\n')
        with open(fn, 'a+') as f:

            t0 = time.perf_counter()
            f.write('*************************************NEW RUN PEGELALARM.AT*************************************')
            now = datetime.now()
            txt = f'\n\nProgram run on the following date: {now}'
            print(txt)
            f.write(txt)
            r = RL(self.Get_)
            r.Request()
            self.Process_df = utils.Process(r.Request_df,self.Get_)
            txt = self.Update( )
            print(txt)
            f.write(txt)

            t1 = time.perf_counter()

            txt = f"\n\nEnd of the update for PEGELALARM.AT. The running time was {round(t1-t0)} s"
            print (txt)
            f.write(txt)
            txt = "\n\n#####################################END PEGELALARM.AT######################################"
            print (txt)
            f.write(txt)

def app_update (git_commit: bool , github_push : bool ):
    #repo = git.Repo('.', search_parent_directories=True)
    repo = 'D:/Repos/PirnaCaseStudy'
    os.chdir(repo)

    '''
    Instantiating the get class
    '''

    database_fn = 'Data/Database.db'
    Get = SMARTControl.queries.Get(database_fn)

    '''
    Update database locally
    '''
    r = RL(Get)
    print(r.url)
    r.Request()
    r.RiverAPItoSQL()
    InowasLongAPItoSQL(Get)

    dt = str(datetime.now()).\
    replace(':','-').\
        split('.')[0]
    commit_message = f'Database_LastUpdate-{dt}'

    if git_commit:
        '''
        Commit database locally
        '''
        repo = git.Repo('.', search_parent_directories=True)
        path = repo.working_tree_dir
        # path = 'D:/Repos/PirnaCaseStudy'

        contents = os.listdir(path+'/Data')
        database_list = ['Data/'+file for file in contents if 'db' in file or 'LOG' in file]

        # for i in range(len(database_list)):
            # repo.git.add(database_list[i])
        repo.git.add(all=True)
        repo.git.commit( '-m', commit_message)

    if github_push:
        '''
        Add to Github
        '''
        with open('D:\Repos\AccessToken.txt', 'r') as f:
        	token = f.readline()

        g = Github(token)
        repo = g.get_user().get_repo('PirnaStudyCase')
        contents = repo.get_contents('Data')
        database_list = [file for file in contents if 'db' in file.path or 'LOG' in file.path]

        for i in range(len(database_list)):
        	repo.update_file(database_list[i].path, commit_message, commit_message, database_list[i].sha)


# 點擊做法
import win32api, win32gui, win32con, win32com.client
import time
import pyautogui
import pythoncom
import json
from Models.Visual import Visual

class clickModel:
    def __init__(self, path, app):
        self.path = path
        self.app = app

        self.times = 0
        self.hwnd = ''
        self.innerHwnd = ''
        self.connect = ''

        self.apple = 'None'

    def doClick(self, position):
        print('click')
        x = position[0]
        y = position[1]
        hwnd = self.innerHwnd
        long_position = win32api.MAKELONG(int(x), int(y))
        win32gui.SendMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
        win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, long_position)
        # time.sleep(0.5)
        win32gui.SendMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)
        win32gui.PostMessage(hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, long_position)
        time.sleep(0.5)

    def doClick_pixel(self, position):
        before_img = self.Visual.get_900_506_image()
        x = position[0]
        y = position[1]
        before_pixel = self.Visual.image_pixel(before_img, x, y)

        self.doClick(position)
        time.sleep(1)

        after_img = self.Visual.get_900_506_image()
        after_pixel = self.Visual.image_pixel(after_img, x, y)

        while before_pixel == after_pixel:
            time.sleep(1)
            self.doClick(position)

            after_img = self.Visual.get_900_506_image()
            after_pixel = self.Visual.image_pixel(after_img, x, y)

    def doClick_cutImage(self, position, x_start, y_start, x_end, y_end):
        def click_check(before, after):
            return self.Visual.different_image_check(before, after)

        before = self.Visual.get_900_506_image()
        before_cut = self.Visual.image_cut(before, x_start, y_start, x_end, y_end)
        while True:
            self.doClick(position)
            time.sleep(1)
            after = self.Visual.get_900_506_image()
            after_cut = self.Visual.image_cut(after, x_start, y_start, x_end, y_end)
            if click_check(before_cut, after_cut):
                break
            time.sleep(1)

    def window_to_front(self):
        shell = win32com.client.Dispatch("WScript.Shell")
        shell.SendKeys('%')
        win32gui.SetForegroundWindow(self.hwnd)

    def status_init(self, now_status_support, now_status_skill, apple, hwnd, innerHwnd):
        pythoncom.CoInitialize()
        self.hwnd = hwnd
        self.innerHwnd = innerHwnd
        self.apple = apple

        self.Visual = Visual(self.hwnd, self.innerHwnd, self.path, self.app)

        self.position()
        if now_status_support != '':
            self.supporter = now_status_support
        else:
            with open(rf'{self.path}\Configs\support_init.json','r') as fr:
                self.supporter = json.load(fr)

        if now_status_skill != '':
            self.battleSkill = now_status_skill
        else:
            with open(rf'{self.path}\Configs\battleSkill_init.json','r') as fr:
                self.battleSkill = json.load(fr)

        self.window_to_front()

    def now_step(self):
        self.step = 0
        n1 = self.CheckBattleCount(1)
        n2 = self.CheckBattleCount(2)
        n3 = self.CheckBattleCount(3)
        n = self.Visual.locateOnImage('clothes')
        if n != None:
            print(n1, n2, n3)
            if n1 != None:
                self.step = 1
            if n2 != None:
                self.step = 2
            if n3 != None:
                self.step = 3
        print('now step is', self.step)

    def runScrip(self):

        while self.step != 4:
            if self.step == 0:
                self.go_again()

            if self.step == 0:
                self.over_limit()

            if self.step == 0:
                self.AP_recovery()

            if self.step == 0:
                self.select_support()
                self.step += 1

            if  self.step == 1:
                self.Battle(1)
                self.step += 1

            if  self.step == 2:
                self.Battle(2)
                self.step += 1

            if  self.step == 3:
                self.Battle(3)
                self.step += 1

            if self.step == 4:
                print('battle end')
                self.step = 0
                break

    def position(self):
        x = win32gui.GetWindowRect(self.innerHwnd)[0]
        y = win32gui.GetWindowRect(self.innerHwnd)[1]
        x_long = win32gui.GetWindowRect(self.innerHwnd)[2] - win32gui.GetWindowRect(self.innerHwnd)[0]
        y_long = win32gui.GetWindowRect(self.innerHwnd)[3] - win32gui.GetWindowRect(self.innerHwnd)[1]

        self.playerSkill = {'p0s1': [x_long * 0.705, y_long * 0.425], 'p0s2':[x_long * 0.775, y_long * 0.425], 'p0s3': [x_long * 0.845, y_long * 0.425],
                            'p1s1': [x_long * 0.055, y_long * 0.8], 'p1s2': [x_long * 0.125, y_long * 0.8], 'p1s3': [x_long * 0.195, y_long * 0.8],
                            'p2s1': [x_long * 0.305, y_long * 0.8], 'p2s2': [x_long * 0.375, y_long * 0.8], 'p2s3': [x_long * 0.445, y_long * 0.8],
                            'p3s1': [x_long * 0.555, y_long * 0.8], 'p3s2': [x_long * 0.625, y_long * 0.8], 'p3s3': [x_long * 0.695, y_long * 0.8]}

        self.attack = [x_long * 0.89, y_long * 0.8]
        self.clothes = [x_long * 0.935, y_long * 0.425]

        self.toPlayer = {'player1': [x_long * 0.265, y_long * 0.655],'player2': [x_long * 0.5, y_long * 0.655],'player3': [x_long * 0.735, y_long * 0.655]}
        self.clothesSkill = [[x_long * 0.705, y_long * 0.425], [x_long * 0.775, y_long * 0.425], [x_long * 0.845, y_long * 0.425]]
        self.big = [[x_long * 0.325, y_long * 0.32], [x_long * 0.5, y_long * 0.32], [x_long * 0.675, y_long * 0.32]]
        self.small = [[x_long * 0.1, y_long * 0.743], [x_long * 0.3, y_long * 0.743], [x_long * 0.5, y_long * 0.743], [x_long * 0.7, y_long * 0.743], [x_long * 0.9, y_long * 0.743]]
        self.middle = [x_long * 0.5, y_long * 0.5]
        self.next = [x_long * 0.87, y_long * 0.895]
        self.mission = [x_long * 0.7, y_long * 0.26]

    def Battle(self, n): # battle n
        self.main_screen(5)
        print('now is battle', n)

        while True:
            battle_count_position = self.CheckBattleCount(n)
            print(battle_count_position)

            if battle_count_position == None: # 不是 battle n 就離開
                break

            else:
                for i in range(4):
                    for j in range(1, 4, 1):
                        p = 'b' + str(n) + 'p' + str(i) + 's' + str(j)
                        m = self.battleSkill[p]

                        if m != 'None':
                            self.useSkill(i, j, m)
                            time.sleep(1)

                while True:
                    self.main_screen(5)
                    self.useAttack(n)
                    self.main_screen(5)
                    battle_count_position = self.CheckBattleCount(n)
                    with_servant_connect_position = self.Visual.locateOnImage('with_servant_connect')

                    if battle_count_position == None:
                        break
                    elif with_servant_connect_position != None:
                        break
                break

        print('battle ', n, ' end')

    def endBattle(self):
        if self.main_screen(5) == 'with_servant_connect':
            while True:
                with_servant_connect_position = self.Visual.locateOnImage('with_servant_connect')
                self.doClick(with_servant_connect_position)

                time.sleep(1)
                masterEXP_position = self.Visual.locateOnImage('masterEXP')

    def main_screen(self, t):
        clothes_position = self.Visual.locateOnImage('clothes')
        with_servant_connect_position = self.Visual.locateOnImage('with_servant_connect')

        while True:
            if clothes_position != None: # 畫面上有 '御主技能'
                return 'clothes'
            elif with_servant_connect_position != None: # 畫面上有 '與從者的羈絆'
                return 'with_servant_connect'
            else:
                time.sleep(t)
                clothes_position = self.Visual.locateOnImage('clothes')
                with_servant_connect_position = self.Visual.locateOnImage('with_servant_connect')

    def useSkill_(self, p, n, m): # player p 的第 n 個技能, 給player m
        if m != 'None':
            player = 'p' + str(p) + 's' + str(n)
            print(player)
            if p == 0:
                clothes_position = self.Visual.locateOnImage('clothes')
                while True:
                    if clothes_position != None:
                        time.sleep(1.5)
                        # self.doClick(self.clothes)
                        self.sure_doClick(self.clothes)
                        time.sleep(1)
                        break
                    else:
                        time.sleep(0.5)
                        clothes_position = self.Visual.locateOnImage('clothes')

            # self.doClick(self.playerSkill[player])
            self.sure_doClick(self.playerSkill[player])
            time.sleep(4)

            if self.skill_used_check() != None:
                self.doClick(self.skill_used_check())
                time.sleep(2)
                if p == 0:
                    # self.doClick(self.clothes)
                    self.sure_doClick(self.clothes)
                    time.sleep(1)
            else:
                Please_select_object_position = self.Visual.locateOnImage('Please_select_object')
                if Please_select_object_position != None:
                    # self.doClick(self.toPlayer[m])
                    self.sure_doClick(self.toPlayer[m])
                time.sleep(4)

    def useSkill(self, p, n, m):
        if m != 'None':

            if p == 0:
                self.useClothesSkill(n, m)

            else:
                self.usePlayerSkill(p, n, m)
        print('finish one skill')

    def useClothesSkill(self, n, m):
        player = 'p0s' + str(n)

        self.main_screen(2)
        print('click clothes')
        time.sleep(2)
        self.doClick_cutImage(self.clothes, 600, 200, 800, 250)
        time.sleep(2)

        # self.doClick_pixel(self.playerSkill[player])
        self.doClick(self.playerSkill[player])
        time.sleep(4)

        # while True:

        if self.please_select_object() != None:
            self.sure_doClick(self.toPlayer[m])
            # break

        elif self.skill_used_check() != None:
            self.sure_doClick(self.skill_used_check())
            self.doClick_cutImage(self.clothes, 600, 200, 800, 250)
            # break

            # else:
            #     self.doClick_cutImage(self.playerSkill[player], 600, 200, 800, 250)
            #     time.sleep(2)

    def usePlayerSkill(self, p, n, m):
        player = 'p' + str(p) + 's' + str(n)

        self.main_screen(2)
        before_pixel = self.position_pixel(self.playerSkill[player])
        self.doClick(self.playerSkill[player])
        time.sleep(2)
        after_pixel = self.position_pixel(self.playerSkill[player])

        while True:

            if self.please_select_object() != None:
                self.sure_doClick(self.toPlayer[m])
                break

            elif self.skill_used_check() != None:
                self.sure_doClick(self.skill_used_check())
                break

            elif before_pixel != after_pixel:
                break

            else:
                self.doClick(self.playerSkill[player])
                time.sleep(2)
                after_pixel = self.position_pixel(self.playerSkill[player])

    def please_select_object(self):
        Please_select_object_position = self.Visual.locateOnImage('Please_select_object')
        if Please_select_object_position != None:
            return Please_select_object_position
        else:
            return None

    def skill_used_check(self):
        # Now = self.Visual.get_900_506_image()
        # cancel = rf'{self.path}\img\cancel.png'

        cancel_position = self.Visual.locateOnImage('cancel')
        if cancel_position != None:
            return cancel_position
        else:
            return None

        # if pyautogui.locate(cancel, Now, confidence=0.8) != None :
        #     print(pyautogui.locate(cancel, Now, confidence=0.8))
        #     x = pyautogui.locate(cancel, Now, confidence=0.8)[0]
        #     y = pyautogui.locate(cancel, Now, confidence=0.8)[1]
        #     return [x, y]
        # else:
        #     return None

    def position_pixel(self, position):
        pixel = self.Visual.get_pixel(position)
        return pixel

    def useAttack(self, n):
        self.position()

        p1np = 'b' + str(n) + 'p1NP'
        p2np = 'b' + str(n) + 'p2NP'
        p3np = 'b' + str(n) + 'p3NP'
        np_list = [self.battleSkill[p1np], self.battleSkill[p2np],self.battleSkill[p3np]]

        while True:
            self.doClick(self.attack)
            time.sleep(2)

            for i, p in enumerate(np_list):
                if p == 'ON':
                    self.doClick(self.big[i])
                    time.sleep(1)

            for j in range(3):
                self.doClick(self.small[j])
                time.sleep(1)

            go_back = self.wait_until('go_back', 5, 1)
            if go_back != None:
                self.sure_doClick(go_back)
                time.sleep(2)

            else:
                time.sleep(10)
                break

    def CheckBattleCount(self, count):
        Now = self.Visual.get_900_506_image()
        Now_cut = self.Visual.image_cut(Now, 620, 0, 633, 30)
        Now_cut_black = self.Visual.WordToBlack(Now_cut)
        BattleCount = rf'{self.path}\img\BattleCount\Battle{count}.png'

        if pyautogui.locate(Now_cut_black, BattleCount, confidence=0.8) != None :

            x = pyautogui.locate(Now_cut_black, BattleCount, confidence=0.8)[0]
            y = pyautogui.locate(Now_cut_black, BattleCount, confidence=0.8)[1]
            return [x, y]
        else:
            return None

    def select_support(self):
        old_type = self.supporter['type']
        old_character = self.supporter['character']
        while True:
            time.sleep(2)
            # support_choose_position = self.locateOnImage(rf"\Support\support_choose", 'ScreenShot')

            support_choose_position = self.Visual.locateOnImage(rf"\Support\support_choose")

            if support_choose_position != None:
                break

        # support_type_position = self.locateOnImage(rf"\Support\{self.supporter['type']}", 'ScreenShot')

        support_type_position = self.Visual.locateOnImage(rf"\Support\{self.supporter['type']}")

        self.doClick(support_type_position)

        # supporter_position = self.locateOnImage(rf"\Support\{self.supporter['type']}\{self.supporter['supporter']}", 'ScreenShot')

        supporter_position = self.Visual.locateOnImage(rf"\Support\{self.supporter['type']}\{self.supporter['character']}")

        print(supporter_position)

        if supporter_position != None:
            time.sleep(1)
            self.doClick(supporter_position)
        else:
            i = 0
            k = 0
            while True:
                i += 1
                # go_down_position = self.locateOnImage(rf"\Support\go_down", 'ScreenShot')

                go_down_position = self.Visual.locateOnImage(rf"\Support\go_down")

                if go_down_position == None or i == 5:
                    k += 1
                    if k == 3:

                        self.supporter['type'] = 'all'
                        self.supporter['character'] = 'Random'
                    # re_new_list_position = self.locateOnImage(rf"\Support\re_new_list", 'ScreenShot')

                    re_new_list_position = self.Visual.locateOnImage(rf"\Support\re_new_list")

                    self.sure_doClick(re_new_list_position)
                    time.sleep(1)
                    # yes_position = self.locateOnImage(rf"\Support\yes", 'ScreenShot')

                    yes_position = self.Visual.locateOnImage(rf"\Support\yes")

                    self.doClick(yes_position)
                    time.sleep(5)
                    i = 0

                    support_type_position = self.Visual.locateOnImage(rf"\Support\{self.supporter['type']}")

                    self.doClick(support_type_position)

                else:
                    self.doClick(go_down_position)
                    time.sleep(2)

                # supporter_position = self.locateOnImage(rf"\Support\{self.supporter['type']}\{self.supporter['supporter']}", 'ScreenShot')

                supporter_position = self.Visual.locateOnImage(rf"\Support\{self.supporter['type']}\{self.supporter['character']}")

                if supporter_position != None:
                    time.sleep(1)
                    self.doClick(supporter_position)
                    break
        time.sleep(1)
        # mission_start_position = self.locateOnImage(rf"\Support\mission_start", 'ScreenShot')

        mission_start_position = self.Visual.locateOnImage(rf"\Support\mission_start")
        clothes_position = self.Visual.locateOnImage('clothes')

        while True:

            if mission_start_position != None:
                self.doClick(mission_start_position)
                break
            elif clothes_position != None:
                break
            else:
                time.sleep(0.5)
                mission_start_position = self.Visual.locateOnImage(rf"\Support\mission_start")
                clothes_position = self.Visual.locateOnImage('clothes')

        self.supporter['type'] = old_type
        self.supporter['character'] = old_character
        print('select end')

    def go_again(self):
        while True:
            go_again_position = self.Visual.locateOnImage(rf"go_again")
            close_position = self.Visual.locateOnImage(rf"close")
            last_time_position = self.Visual.locateOnImage(rf"last_time")

            if go_again_position == None: # 沒有連續出擊
                if close_position == None: # 沒有關閉
                    self.doClick(self.next)
                    time.sleep(0.5)

                else: # 有關閉 -> 選關頁面
                    last_time_position = self.Visual.locateOnImage(rf"last_time")

                    if last_time_position == None: # 沒有上次執行
                        self.doClick(self.mission) # 按最上面那一關
                        break
                    else: # 有上次執行
                        self.doClick(last_time_position) # 按上次執行
                        break
            else: # 有連續出擊
                self.doClick(go_again_position) # 按連續出擊
                break

    def over_limit(self):
        over_limit_position = self.Visual.locateOnImage(rf"over_limit")
        if over_limit_position != None:
            print('over limit')
            self.step = 4
            return True

    def AP_recovery(self):

        AP_position = self.Visual.locateOnImage(rf"\AP_Recovery\AP")

        if AP_position != None:
            if self.apple == 'None':
                self.step = 4

            else:
                go_down_position = self.wait_until(rf"\AP_Recovery\go_down", 5, 1)
                if go_down_position != None:
                    self.sure_doClick(go_down_position)
                    time.sleep(1)

                    apple_position = self.Visual.locateOnImage(rf"\AP_Recovery\{self.apple}")
                    if apple_position != None:
                        self.sure_doClick(apple_position)
                        sure_position = self.wait_until(rf"\AP_Recovery\sure", 10, 1)
                        self.sure_doClick(sure_position)

                else:
                    self.step = 0

    def wait_until(self, item, n, t):
        item_position = self.Visual.locateOnImage(item)

        for i in range(n):
            if item_position != None:
                return item_position
            else:
                time.sleep(t)
                item_position = self.Visual.locateOnImage(item)

        return None

    def sure_doClick(self, position):
        def click_check(before, after, ):
            return self.Visual.different_image_check(before, after)

        before = self.Visual.get_900_506_image()
        while True:
            self.doClick(position)
            after = self.Visual.get_900_506_image()
            # before.show()
            # after.show()
            if click_check(before, after):
                break
            time.sleep(1)

    def no_control(self, innerHwnd):
        win32gui.EnableWindow(innerHwnd, False)
        print('F')

    def control(self, innerHwnd):
        win32gui.EnableWindow(innerHwnd, True)
        print('T')


"""
saved.py
"""
from flask import Blueprint, request, redirect, url_for, jsonify

# pylint: disable=unused-import
from flask_login import (
    login_required,
    current_user,
)

# pylint: disable=import-error
from utils.models import db, Users, Saved, Posts

saved = Blueprint("saved", __name__)

@saved.route("/load_saved", methods=["POST", "GET"])
# @login_required
def load_saved():
    """
    queries the saved posts of the logged in user
    """

    # query the Saved table by the user id that is logged in
    # saves = Saved.query.filer_by(user_id=current_user.id)

    # place holder
    saves = Saved.query.filter_by(user_id=current_user.id)

    saves_list = []

    # add each saved post's data to the saves_list
    for save in saves:
        post = Posts.query.filter_by(id=save.post_id).first()
        data = {
            "user_id": save.user_id,
            "post_id": post.id,
            "title": post.title,
            "caption": post.caption,
        }
        saves_list.append(data)

    # get the user name that is logged in, try to replace this later, kinda jank
    # user = Users.query.get(current_user.id).user
    user = "placeholder"

    return jsonify({"saves": saves_list, "user": user})

@saved.route("/save_post", methods=["POST"])
def save_post():
    post_id = request.form["post_id"]
    # pylint: disable=invalid-name
    # no error
    savedPosts = Saved.query.all()

    # if post already saved, return
    for post in savedPosts:
        if post.user_id == current_user.id and post.post_id == post_id:
            return redirect(url_for("bp.index"))

    new_saved = Saved(post_id=post_id, user_id=current_user.id)

    db.session.add(new_saved)
    db.session.commit()

    return redirect(url_for("bp.index"))
    # return redirect(url_for("bp.home"))s