Tuesday 28 February 2023

Add external form elements to FormData

In HTML5 we can use the form attribute on form control elements to have them be part of a form other then the one they're childs of, as described in this answer.

These elements however seem not to be considered when passing the form element to the FormData constructor, as described on MDN.

Is there a way to have these 'external' controls be included in a way that I then can send the complete form data via a fetch Request?

I am aware, that I could loop through the external elements and append them to the FormData instance like so

let formData = new FormData(form);

for (const external of document.body.querySelectorAll('[form="' + form.name + '"]')) {
    formData.append(external.name, external.value);
}

However I hoped for a more convenient way, especially taking in consideration the extra cases and checks I would have to make it work with radiobuttons etc.



from Add external form elements to FormData

Authenticate a GET request to Google Play Purchase API with service account python

I need to verify purchases of my android App from my AWS lambda in python.

I have seen many posts of how to do so and the documentation and here is the code I have written :

url = f"{google_verify_purchase_endpoint}/{product_id}/tokens/{token}"
response = requests.get(url=url)
data = response.json()
logging.info(f"Response from Google Play API : {data}")

When I do so, it throws a 401 status code not allowed. Alright, I have created a service account to allow the request with OAuth, but how can I use it to allow the request ?

Unfortunately I can't use the google-api-python-client as mentioned here which is too big for my AWS lambda maximum size of 250Mb unzipped package.

So my question is to use the service account with a simple GET requests or how can I authenticate automatically without the google-api-python-client ?

Thanks in advance



from Authenticate a GET request to Google Play Purchase API with service account python

UDP hole punching in python, works locally but not through AWS

I'm trying to implement UDP hole punching so that I can connect 2 clients together that are not in the same network without the need to portforward. I'm using the top answer in this question as my basis and it works if I run the clients and the server on my local machine (both clients get the "hello" response).

Now I've made an AWS instance that is running the server and opened the ports on it so my clients can connect, however when they've received the ip + port for each other they seem to not reach (they get the response "peer: x.x.x.x yyyy", but not the "hello" response). What am I missing in order to make them capable of communicating? even if both clients are on the same machine it still doesn't work, it only gives the correct response when using the local host (127.0.0.1) for both the client and the server.



from UDP hole punching in python, works locally but not through AWS

I am struggling to save data into postgreSql table using typeORM and Nestjs with one to many relationship

I have 2 entities i.e Customer, Orders with one to many relationship. A customer can have multiple orders and an order can be owned by only one customer. I am creating an api to create order that is a POST api.

order.controller.ts

@Post()
async createOrder(@Body() order: CreateOrderDto) {
  const newOrder = await this.orderService.createOrder(order);
  return newOrder;
}

order.service.ts

async createOrder(order: IOrder) {
  const newOrder = this.orderRepository.create(order);
  await this.orderRepository.save(newOrder);
  return newOrder;
}

create-order.dto.ts

export class CreateOrderDto {
 @IsString()
 @IsNotEmpty()
 @Length(5, 400)
 public description: string;

 @IsNotEmpty()
 @IsNumber()
 @IsPositive()
 public amount: number;

 @IsNotEmpty()
 @IsString()
 public paymentMethod: string;

 @IsOptional()
 @IsString()
 public customizeName: string;

 @IsOptional()
 @IsString()
 public weight: string;

 @IsNotEmpty()
 public customer: ICustomer;

 @IsNotEmpty()
 @IsArray()
 public products: Array<IProduct>;
}

I am sending this json from postman

{
   "description": "Customized waullet",
   "quantity": 1,
   "amount": 1500,
   "paymentMethod": "cashOnDelivery",
   "customizeName": "Faizan",
   "weight": "2.5g",
   "customer": {
       "fullName": "Anas Subzwari",
       "age": 20,
       "country": "Pakistan",
       "city": "Karachi",
       "address": "B-17/519, Indus Mehran Society",
       "contactNumber": "+923132953154"
   },
  "products": [
     {
        "name": "test10 product",
        "description": "Customize blue color vaaulid with picture printing",
        "cost": 700,
        "price": 1200,
        "weight": "2.3g"
    },
    {
        "name": "Customize vaulid",
        "description": "Customize blue color vaaulid with picture printing",
        "cost": 700,
        "price": 1200,
        "weight": "2.3g"
    }
  ]
}

I am getting this error

UpdateValuesMissingError: Cannot perform update query because update values are not defined. Call "qb.set(...)" method to specify updated values.

{"statusCode":500,"timestamp":"2023-02-25T18:06:43.623Z","path":"/orders","method":"POST","errorName":"UpdateValuesMissingError","message":"Cannot perform update query because update values are not defined. Call \"qb.set(...)\" method to specify updated values."}

How can i fix this error?



from I am struggling to save data into postgreSql table using typeORM and Nestjs with one to many relationship

How to make an azure function to read from the dist folder generated by Typescript?

I have an azure function that works great in JS but I've migrated the code to TS. When I run the deployment I do see the dist folder generated by TS, however, if I go to the Azure portal I still see my old JS function but there's no sign of the dist folder. I have confirmed through Kudu that the dist folder exists and it contains the new code.

How can I let know to the Azure function to read from the dist folder?

This is my current folder structure:

- Project
  - azure_func
    - main_func_app
      - funcs
        -func_js
           index.js
           my_ref_vals.json
           function.json
        -func_ts
           index.ts
           my_ref_vals.json
           function.json
        -dist
         -func_ts
            index.js
            index.js.map
       package.json
       host.json
       tsconfig.json

Contents of the tsconfig.json

{
    "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "outDir": "dist",
    "rootDir": ".",
    "sourceMap": true,
    "strict": true
    },
    "include": ["./**/*.ts"],
    "exclude": ["dist", "node_modules"]
}

Contents of the package.json

{
"version": "0.0.0",
"description": "",
"scripts": {
  "build": "tsc",
  "watch": "tsc -w",
  "prestart": "npm run build",
  "start": "func host start --typescript",
  "test": "echo \"No tests yet...\"",
  "lint": "eslint .",
  "format": "prettier --write ."
},
"devDependencies":{
  "@types/jest": "^28.1.6",
  "@types/node": "16.x",
  "@typescript-eslint/eslint-plugin": "^5.31.0",
  "@typescript-eslint/parser": "^5.31.0",
  "eslint": "^8.20.0",
  "eslint-config-prettier": "^8.5.0",
}
}

Deployment file:

- stage: Build
  displayName: Build stage for Azure Function
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)

    steps:
      - task: NodeTool@0
        inputs:
          versionSpec: '14.x'
          displayName: 'Install Node.js'

      - task: AzureCLI@2
        inputs:
          azureSubscription: 'mysub-test-group-SPN'
          scriptType: 'bash'
          scriptLocation: 'inlineScript'
          inlineScript: |
            npm install
            npm run build

      - task: ArchiveFiles@2
        displayName: 'Create artifact'
        inputs: 
           rootFolderOrFile: 'Project/azure_func/main_func_app/funcs'
           includeRootFolder: false
           archiveType: zip
           archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
           replaceExistingArchive: true

      - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
        displayName: 'Publish Azure Functions Artifact'
        artifact: functions

      - task: AzureFunctionApp@1
        displayName: 'Deploy Azure Functions'
        inputs:
          azureSubscription: 'mysub-test-group-SPN'
          appType: 'functionApp'
          appName: 'azure_funcs'
          package: '$(Pipeline.Workspace)/functions/$(Build.BuildId).zip'
          deploymentMethod: 'auto'

Please, comment if something isn't clear.



from How to make an azure function to read from the dist folder generated by Typescript?

Is there any better way to use ratios?

I am looking to predict the number of cars that will be parked in my 100-car capacity lot over the next seven days. To price the rental accordingly, I am analyzing the reservations from the past seven days and how they ultimately played out for each day. By using this data, I can determine the expected demand for each day. For instance, if I had 15 reservations on the fifth day seven days ago and final reservation for that day was 60, and now have 25 reservations for the 5th day in future, I can assume that the fifth day has increased in demand by a fraction of 25/15 and my future demand is 25*60/15 = 100.

The problem in this case is sometime my model predict more than 100 reservations despite the fact that my capacity is 100 for each day. Is there better way to scale and project the demand, I don’t wish to use a hard limit of 100 in the code?

Code below:

from datetime import datetime, timedelta, date

# Historical data
historical_data = {
    '18-Feb-23': (35, 38),
    '19-Feb-23': (40, 50),
    '20-Feb-23': (35, 50),
    '21-Feb-23': (28, 50),
    '22-Feb-23': (15, 65),
    '23-Feb-23': (10, 54),
    '24-Feb-23': (3, 70)
}

# Future data
future_data = {
    '25-Feb-23': (40, None),
    '26-Feb-23': (35, None),
    '27-Feb-23': (28, None),
    '28-Feb-23': (20, None),
    '1-Mar-23': (25, None),
    '2-Mar-23': (3, None),
    '3-Mar-23': (1, None)
}

# Calculate expected demand for each day
for date in future_data:
    # Get the corresponding date seven days ago
    past_date = (datetime.strptime(date, '%d-%b-%y') - timedelta(days=7)).strftime('%d-%b-%y')
    
    # Calculate the fraction of increase in demand from past to future
    if past_date in historical_data:
        past_reservations = historical_data[past_date][0]
        past_final_reservations = historical_data[past_date][1]
        future_reservations = future_data[date][0]
        fraction_increase = future_reservations / past_reservations
    else:
        # If past data is not available, assume no change in demand
        fraction_increase = 1.0
    
    # Calculate the expected final reservations for the future date
    if fraction_increase > 0:
        expected_final_reservations = int(round(past_final_reservations * fraction_increase))
    else:
        expected_final_reservations = past_final_reservations
    
    future_data[date] = (future_data[date][0], expected_final_reservations)

# Print the predictions
for date in future_data:
    print(f"{date}: Expected final reservations = {future_data[date][1]}")


from Is there any better way to use ratios?

Access ipynb files from shared drive using local Jupyter notebook

Currently, I have my python jupyter notebook installed in my laptop.

So, am able to see two .exe files such as jupyter-dejavue.exe and jupyter-nbconvert.exe under the below path

C:\Users\test\AppData\Roaming\Python\Python38\Scripts\

Currently, I have been asked to move all my code files to the company network shared drive which looks like below

\\ANZINDv024.abcde.com\GLOBAL_CODE_FILES

So, when I launch Jupyter notebook from my start menu (in my laptop), am not able to navigate to the shared drive in the below screen (because I don't see the shared drive folder)

enter image description here

So, I went to my shared drive and double-clicked .ipynb files but it opens in browser (with text format).

So, I chose open with and tried opening with jupyter-dejavue.exe and jupyter-nbconvert.exe but both doesn't launch the jupyter notebook.

How to launch Jupyter notebook to run .ipynb files stored in shared drive?



from Access ipynb files from shared drive using local Jupyter notebook

Monday 27 February 2023

Should I worry about conflicts with possible future ECMAScript changes if I want to add a property to a Function object with a generic name?

I am trying to create a function which can be invoked through the following ways:

log() 
log.error()

Based on my understanding, we can creating such a function by writing on the function itself:

function log(){

}
log.error = () => {}

is it not recommended writing on the function this way? Because JavaScript might release an update in the future which might hold the "error" property, then my "error" property will be overriding this JavaScript feature.


Update:

I've been asked to provide an example of where people might be using this pattern and why it's useful.

For example, Jquery:

$( "#button-container button" ).on( "click", function( event ) {} )

and

$.ajax({  url: "/api/getWeather" });

you see this pattern in many popular libraries.

the $ is an object,

it's callable, so you can invoke it $('css query selector here') and it's chainable, so you can call other methods on it $().on(), and it has methods on it $.ajax()



from Should I worry about conflicts with possible future ECMAScript changes if I want to add a property to a Function object with a generic name?

3DS Security check in MasterCard Gateway

Im trying to implement payments using MasterCard Gateway for android i follow the steps in the documentation i reach the step where i get the HTML Response for 3DS i use testing card to check it but the issue when i click submit in the 3ds check i got authorization error i will attach images below, Anyone know why i got this error, I know the error says that's an authorization issue but i got the html reponse using the api passowrd and merchantId, What other thing im missing here?

If anyone used this sdk before i will be happy to know what's the steps he follows because the documentation says nothing and just gives some headlines to implement the sdk.

Thanks all.

enter image description here

error i got



from 3DS Security check in MasterCard Gateway

How to Create a Restricted File Browser in Python for Windows

I’d like to create a restricted folder/ file explorer in Python (I have version 2.7.9, but I don’t mind changing that) for Windows.

Essentially, I want to initially specify the folder to which the code opens. For example, the code should initially open to: C:\Users\myName\Desktop\myDemoFolder (the user must not know this folder simply by looking at the GUI).

The user must be able to browse downwards (deeper into folders) and backwards (but only up to the initial folder to which the code opens). The user must be able to click to open a file (for example: pdf), and the file must automatically open in its default application.

An example of what I’d like is presented in figure 1. (The look of the interface is not important)

Figure 1: What I would like to get

Currently, I am able to get figure 2 using the code presented here:

from Tkinter import Tk
from tkFileDialog import askopenfilename

Tk().withdraw() 
filename = askopenfilename()
print(filename)

Figure 2: What I currently have

Research has indicated that it is not possible to change the default buttons in Tkinter windows. Is this true? If it can’t be done with Tkinter (and that’s fine), how else can we do it?

I’d happily choose simple, non-Tkinter code (perhaps using wxPython’s wx.GenericDirCtrl()) rather than elaborate Tkinter code, but no restrictive libraries please.

A modular design approach is not needed. I’d rather have simple (functional) code that is shorter than object-oriented code.



from How to Create a Restricted File Browser in Python for Windows

Simulate click event on react element

I'm trying to simulate a .click() event on a React element but I can't figure out why it is not working (It's not reacting when I'm firing the event).

I would like to post a Facebook comment using only JavaScript but I'm stuck at the first step (do a .click() on div[class="UFIInputContainer"] element).

My code is:

document.querySelector('div[class="UFIInputContainer"]').click();

And here's the URL where I'm trying to do it: https://www.facebook.com/plugins/feedback.php...

P.S. I'm not experienced with React and I don't know really if this is technically possible. It's possible?

EDIT: I'm trying to do this from Chrome DevTools Console.



from Simulate click event on react element

How to solve 'Permission denied error' when creating a Process for .armv8 file in .NET MAUI Android Emulator

I have a .NET MAUI mobile-project and I am trying to call code that has been compiled from C++, namely an .armv8 file. I am trying the Process class to get this to work.

I have tried the following steps:

  1. Save the .armv8-file as an Embedded resource in my project and set Copy to output directory to Copy if newer. The file is in a folder in the mobile-project (so not in de platforms part of the project, but in a folder in the root of the project).

File settings

  1. Request the necessary file-permission (read & write) for Android:
PermissionStatus status = await Permissions.RequestAsync<Permissions.StorageRead>();
        PermissionStatus status2 = await Permissions.RequestAsync<Permissions.StorageWrite>();

These are granted successfully.

  1. Copy the file to the Android specific AppData folder, using the following code:
private string ExtractFiles()
        {
            var assembly = typeof(App).GetTypeInfo().Assembly;

            foreach (var res in assembly.GetManifestResourceNames())
            {
                if (res.Contains(".armv8"))
                {
                    // read source file 
                    using var stream = assembly.GetManifestResourceStream(res);
                    using var reader = new StreamReader(stream);
                    var content = reader.ReadToEnd();

                    // generate target path inside data folder
                    string targetFile = System.IO.Path.Combine(FileSystem.Current.AppDataDirectory, res);

                    // write file into data folder
                    using FileStream outputStream = System.IO.File.OpenWrite(targetFile);
                    using StreamWriter streamWriter = new StreamWriter(outputStream);

                    streamWriter.Write(content);

                    return targetFile;
                }
            }

            return string.Empty;
        }
  1. Tried to change the file-permissions to open with the following code (parameter is the output from step 2):
 public partial void SetPermissionToFile(string path)
        {
            string[] cmd = { "chmod", "744", path };
            Java.Lang.Runtime? runtime = Java.Lang.Runtime.GetRuntime();

            runtime?.Exec(cmd);
        }
  1. Starting a process with the file, with the following code:
_processStartInfo = new ProcessStartInfo
            {
                FileName = path,
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true
            };
            _process = new Process { StartInfo = _processStartInfo };
            _process.Start();

When the _process.Start() method gets called, I am getting a Permission denied error, looking like this:

Permission denied exception

Is this because I am using an emulator? Or is the file maybe in the wrong directory for the Process to work with?

I saw a similar post that got it worked out, but that does not seem to work for me yet: https://chess.stackexchange.com/questions/17685/how-to-send-uci-messages-from-c-app-to-stockfish-on-android/17856#17856?newreg=ea938bb4ac3147878e4fdc29626050cc

Thanks in advance for trying to help me :)



from How to solve 'Permission denied error' when creating a Process for .armv8 file in .NET MAUI Android Emulator

Sunday 26 February 2023

Running different celery tasks (@shared_tasks) on different databases in django multi tenant application

The application I am working on is a multitenant application. I am using celery to run background tasks.

The background tasks that get pushed to Queue (rabbitmq) are getting executed properly when run on the default db setting configured in the settings. But when I submit the background jobs from other tenants, i.e. settings other than default they fail. This is because, in normal sync flow, I am using a custom router that sets the DB to be used based on the request URL( which contains the tenant details). but the same context is lost when the job is submitted as a background task.

Any suggestions ?

I tried using transaction block and passing the db_name as below, but still, it is using the default database only

@shared_task
def run_background_task(task_id, db_name):
    with transaction.atomic(using=tenant_db):
        task = DPTask.objects.get(pk=task_id)

Ideally, the above query should get executed on db_name database settings, but it is happening on default only



from Running different celery tasks (@shared_tasks) on different databases in django multi tenant application

Sharp lines in corners of Threejs BoxGeometry shadows

I made a structure using BoxGeometry, and I have a DirectionalLight rotating around it. The problem is, I can see aliased light bleed in the corners. I've tried updating the shadow.mapSize, the blur radius, the renderer.shadowMap.type, but nothing has worked so far.

shadow aliasing

const gui = new lil.GUI();

// SCENE
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({ antialias: true });


const settings = {
    shadowMapType: "basic",
    physicallyCorrectLights: false,
    shadowMapSize: 2056,
    shadowRadius: 5
}

// CAMERA
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

// RENDERER
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.physicallyCorrectLights = settings.physicallyCorrectLights;
renderer.shadowMap.type = settings.shadowMapType;
renderer.shadowMap.enabled = true;

// CONTROLS
const controls = new THREE.OrbitControls(camera, renderer.domElement);

// RESIZE HAMDLER
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onWindowResize);

// INIT CAMERA
camera.position.z = 35;
camera.position.x = 7.5;
camera.position.y = 13;
camera.lookAt(7.5, 0, -20)

// INIT HEMISPHERE LIGHT
scene.add(new THREE.AmbientLight( 0xffffff, 0.5 ));

// SCENE
scene.background = new THREE.Color(0xffffff);

// FLOOR
const plane = new THREE.Mesh(new THREE.PlaneGeometry(500, 500, 32), new THREE.MeshPhongMaterial({ color: 0xfab74b}));
plane.rotation.x = - Math.PI / 2
plane.receiveShadow = true
scene.add(plane);


const geometry = new THREE.BoxGeometry( 1, 15, 7 );
const material = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} );
const cube = new THREE.Mesh( geometry, material );
cube.position.y = 7.5; 
cube.position.z = -4.5; 
cube.receiveShadow = true
cube.castShadow = true
scene.add( cube );

const geometry2 = new THREE.BoxGeometry( 1, 15, 7 );
const material2 = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} );
const cube2 = new THREE.Mesh( geometry2, material2);
cube2.position.y = 7.5;
cube2.position.z = -4.5; 
cube2.position.x = 16; 
cube2.receiveShadow = true
cube2.castShadow = true
scene.add( cube2 );


//back wall
const geometry3 = new THREE.BoxGeometry( 1, 15, 15 );
const material3 = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} );
const cube3 = new THREE.Mesh( geometry3, material3);
cube3.position.y = 7.5;
cube3.position.x = 8;
cube3.position.z = -7.5 
cube3.rotateY(Math.PI / 2)
cube3.receiveShadow = true
cube3.castShadow = true
scene.add( cube3 );


//top
const geometry4 = new THREE.BoxGeometry( 1, 7, 17 );
const material4 = new THREE.MeshPhongMaterial( {color: 0xFFFFFF} );
const cube4 = new THREE.Mesh( geometry4, material4);
cube4.position.y = 15.5;
cube4.position.x = 8;
cube4.position.z = -4.5 
cube4.rotateY(Math.PI / 2)
cube4.rotateZ(Math.PI / 2)
cube4.receiveShadow = true
cube4.castShadow = true
scene.add( cube4 );




// DIRECTIONAL LIGHT
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.x += 20
directionalLight.position.y += 20
directionalLight.position.z += 20
directionalLight.castShadow = true
directionalLight.shadow.mapSize.width = settings.shadowMapSize;
directionalLight.shadow.mapSize.height = settings.shadowMapSize;
directionalLight.shadow.radius = settings.shadowRadius
// directionalLight.shadow.blurSamples = 500
const d = 25;
directionalLight.shadow.camera.near = 0.5
directionalLight.shadow.camera.far = 60
directionalLight.shadow.camera.left = - d;
directionalLight.shadow.camera.right = d;
directionalLight.shadow.camera.top = d;
directionalLight.shadow.camera.bottom = - d;
scene.add(directionalLight);

scene.add( new THREE.CameraHelper( directionalLight.shadow.camera ) );




// ANIMATE
function animate() {
    // TARGET
    const time = Date.now() * 0.0005;
    directionalLight.position.x = Math.sin(time * 0.7) * 20;
    directionalLight.position.z = Math.cos(time * 0.7) * 20;

    renderer.render(scene, camera);
    requestAnimationFrame(animate);
}
document.body.appendChild(renderer.domElement);
animate();

gui.add( settings, 'shadowMapType', ["basic", "pcf","pcfsoft", "vsm"])
    .onChange( value => {
        switch (value) {
          case 'basic':
            renderer.shadowMap.type = THREE.BasicShadowMap;
            break;
          case 'pcf':
            renderer.shadowMap.type = THREE.PCFShadowMap;
            break;
          case 'pcfsoft':
            renderer.shadowMap.type = THREE.PCFSoftShadowMap;
            break;
          case 'vsm':
            renderer.shadowMap.type = THREE.VSMShadowMap;
            break;
          default:
            console.log('Invalid shadow map type');
        }
        console.log(renderer.shadowMap.type)
    } );
gui.add(settings, "physicallyCorrectLights")
    .onChange( value => {
        renderer.physicallyCorrectLights = settings.physicallyCorrectLights
    })
gui.add( settings, 'shadowMapSize', 0, 12000, 200 )
    .onChange( value => {
        directionalLight.shadow.map.dispose(); // important
        directionalLight.shadow.mapSize = new THREE.Vector2(value,value)
    })

gui.add( settings, 'shadowRadius', 0, 100, 1 )
.onChange( value => {
    directionalLight.shadow.radius = value;
    directionalLight.shadow.updateMatrices(directionalLight);
})
*
{
    margin: 0;
    padding: 0;
}

html,
body
{
    overflow: hidden;
}

.webgl
{
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
}
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.122.0/examples/js/controls/OrbitControls.min.js"></script>

<script src="https://cdn.jsdelivr.net/npm/three-buffer-geometry-utils@1.0.0/index.min.js"></script>

<script src="https://unpkg.com/lil-gui@0.16.1/dist/lil-gui.umd.js"></script>

<!-- <script src="https://cdn.jsdelivr.net/npm/three-orbitcontrols@2.110.3/OrbitControls.min.js"></script> -->




<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Open Box</title>
</head>
<body>
    <canvas class="webgl"></canvas>
</body>
</html>

Here is a demo with an imported open box cube (rather than several box geometries next to each other), it still has the artifacting. https://main--marvelous-monstera-7ef7c2.netlify.app/



from Sharp lines in corners of Threejs BoxGeometry shadows

Using processing.py in Anaconda

On page below I read that processing.py is in fact "add-on called Python Mode":

https://py.processing.org/tutorials/gettingstarted/

Does it mean that I cannot by any means use processing inside my python code - let's say - in some Anaconda IDE like Spyder? And to run some equivalent of processing sketches in python?

In other words: How to run some non-trivial code like setup() and draw() functions from sketches like: https://py.processing.org/tutorials/p3d/ ?



from Using processing.py in Anaconda

How does python version affect Azure Functions?

I'm developing Azure Functions using Python 3.10.10 on my machine, deploying the Function through Azure DevOps which is building the artifact using Python 3.6.8, and the Python Version shown for the Function App host is 3.8.

There was a recent update of Azure Functions Runtime which deprecated Python 3.6. (see breaking changes here).

How does python version affect Azure Functions? How do we keep the versions aligned?



from How does python version affect Azure Functions?

Saturday 25 February 2023

How to override `location.href` in Electron to a custom URL?

Description

Is it possible to modify or override the perceived current location and origin in Electron.js?

I am loading a local .html file in Electron, and I need to manually override location.href, document.domain, and all other references to the current location to point to https://example.com/my/page in order for some external libraries (e.g. reCAPTCHA) to work with the local page.

Is it possible to set the current location to be example.com, without actually making a request to the remote URL, so that any javascript on the page thinks that the current URL is that, instead of app://....?

What I've tried

For my tests, I achieved it by running a MITM proxy that intercepts HTTPS requests, and instead of making a request to the origin, responds with a locally generated html page, which therefore I can inject my actual content into.

However, I don't want to go with this approach, and I was wondering if Electron could natively masquerade as a custom URL while loading local files, and report to any content un the page with the appropriate location/origin.



from How to override `location.href` in Electron to a custom URL?

when incrementing using do transaction it reloads my previous activity

when a user wants to favourite an app with in my app. they click on the favourite button which is button 2. this runs do transaction and increments the firebase node upvote and also adds a true boolean to my users favourite node with in firebase. if they click the button again it removes the increment and sets the favourite value to false... this all works great nut i have a weird bug that happens. everytime i click the favourite button it will restart my previous activity..

there are a few variables ill explain here

user points to the userId of the user.. key points is the genre of app the user clicked appkey is the actual app the user is wanting to download

Edit****

Here is a link to a video of what happens when I click the favourite button.

YouTube short of what's happens

my firebase database apps node looks like this

{
 "Apps": {
"Booster": {
  "ANTBOOSTER": {
    "desc": "",
    "downloads": 30,
    "image": "",
    "tag": "Keep Your phone fast",
    "title": "Ant Booster",
    "upvote": 51,
    "url": ""
  }

then my User node looks like this

"Users": {
"USERID APPEARS HERE": {
  "Advert": false,
  "Favourites": {
    "ANTBOOSTER": true
  },
  "downloads": 0,
  "userid": ""

this is the code ive used to favourite

 private void favourite() {
    FirebaseDatabase db = FirebaseDatabase.getInstance();
    DatabaseReference ref = db.getReference().child("Users").child(user).child("Favourites");
    DatabaseReference likesRef = FirebaseDatabase.getInstance().getReference().child("Apps").child(key).child(app_key);
    String Upvote = "upvote";
    if (!FAVOURITE) {
        FAVOURITE = true;
        updateCounter(FAVOURITE, likesRef, Upvote);
        button2.setText("You liked this app");
        ref.child(app_key).setValue(true);

    } else {
        FAVOURITE = false;
        button2.setText("Favourite");
        updateCounter(FAVOURITE, likesRef, Upvote);

        ref.child(app_key).removeValue();

    }
}

this is the update counter method which is called within favourite

  private void updateCounter(boolean increment, DatabaseReference likeref,String REFString) {
    likeref.child(REFString).runTransaction(new Transaction.Handler() {
        @Override
        public Transaction.Result doTransaction(MutableData mutableData) {
            if (mutableData.getValue() != null) {
                int value = mutableData.getValue(Integer.class);
                if (increment) {
                    value++;
                } else {
                    value--;
                }
                mutableData.setValue(value);
            }
            return Transaction.success(mutableData);
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean b,
                               DataSnapshot dataSnapshot) {
            // Transaction completed
            Log.d(TAG, "likeTransaction:onComplete:" + databaseError);
        }
    });
}

and finally this method checks the app is favourited when the activity is started

  private void updateCounter(boolean increment, DatabaseReference likeref,String REFString) {
    likeref.child(REFString).runTransaction(new Transaction.Handler() {
        @Override
        public Transaction.Result doTransaction(MutableData mutableData) {
            if (mutableData.getValue() != null) {
                int value = mutableData.getValue(Integer.class);
                if (increment) {
                    value++;
                } else {
                    value--;
                }
                mutableData.setValue(value);
            }
            return Transaction.success(mutableData);
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean b,
                               DataSnapshot dataSnapshot) {
            // Transaction completed
            Log.d(TAG, "likeTransaction:onComplete:" + databaseError);
        }
    });
}

finally this is the previous activities code which is a tabbed activity

 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_popular, container, false);
    initVariables(root);


    return root;
}

private void initVariables(View root){

    mlist = root.findViewById(R.id.station_list);
    imageView = root.findViewById(R.id.tv_thumbup);

    getApps();

}
private void getApps() {
    db = FirebaseDatabase.getInstance();
    DatabaseReference reference = db.getReference().child("Apps").child(key);


    Query query = reference.orderByChild("upvote").startAt(50);

     valueEventListener = new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            mUrl.clear();
            mTITLE.clear();
            mIMAGE.clear();
            mUrl.clear();
            mUP.clear();
            mDESC.clear();

            for (DataSnapshot snap : snapshot.getChildren()) {
                String stationkey = snap.getKey();

                String a = snap.child("title").getValue(String.class);
                tag = snap.child("tag").getValue(String.class);
                String c = snap.child("url").getValue(String.class);
                Long d = snap.child("upvote").getValue(Long.class);
                String f = snap.child("image").getValue(String.class);


                APP_KEY.add(stationkey);
                mTITLE.add(a);
                mDESC.add(tag);
                mUrl.add(c);
                mUP.add(String.valueOf(d));
                mIMAGE.add(f);


            }
            titlearray = mTITLE.toArray(new String[mTITLE.size()]);
            desarray = mDESC.toArray(new String[mDESC.size()]);
            uparray = mUP.toArray(new String[mUP.size()]);
            imagearray = mIMAGE.toArray(new String[mIMAGE.size()]);

            AppAdapter appAdapter =
                    new AppAdapter(
                            titlearray,
                            desarray,
                            uparray,
                            imagearray, getContext());
            mlist.setAdapter(appAdapter);

            mlist.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                    String bundledkey = APP_KEY.get(i);
                    String bundledtitle = mTITLE.get(i);
                    String bundledtag = mDESC.get(i);
                    String bundledImage = mIMAGE.get(i);
                    String bundledfav = mUP.get(i);
                    query.removeEventListener(valueEventListener);

                    Intent intent = new Intent(getContext(), AppDownload.class);
                    intent.putExtra("app_key", bundledkey);
                    intent.putExtra("title", bundledtitle);
                    intent.putExtra("tag", bundledtag);
                    intent.putExtra("image", bundledImage);
                    intent.putExtra("favourite", bundledfav);
                    intent.putExtra("type_key", key);
                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    startActivity(intent);

                    startActivity(intent);


                }

            });

        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {

        }
    };
    query.addValueEventListener(valueEventListener);
}
 public void setKey (String string){
    key = string;
 }
 public void setUser(String user){this.user = user;}

Edit

so here is my logcat from when i click the favourite button..also re added the video to show what is actually happening as that didnt seam to work yesterday

2023-02-24 10:40:12.295 20849-20849/com.p9p.h20droidapp 
I/Timeline: Timeline: Activity_launch_request time:673793
2023-02-24 10:40:12.320 20849-20849/com.p9p.h20droidapp 
W/ActivityThread: handleWindowVisibility: no activity for token 
android.os.BinderProxy@de19309
2023-02-24 10:40:12.333 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.334 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.343 20849-20849/com.p9p.h20droidapp I/chatty: 
uid=10362(com.p9p.h20droidapp) identical 5 lines
2023-02-24 10:40:12.343 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.346 20849-20849/com.p9p.h20droidapp 
W/TabLayout: MODE_SCROLLABLE + GRAVITY_FILL is not supported, 
GRAVITY_START will be used instead
2023-02-24 10:40:12.346 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.350 20849-20849/com.p9p.h20droidapp 
I/menu_key: MoviesnShows
2023-02-24 10:40:12.351 20849-20849/com.p9p.h20droidapp 
 D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.363 20849-20849/com.p9p.h20droidapp I/chatty: 
uid=10362(com.p9p.h20droidapp) identical 13 lines
2023-02-24 10:40:12.363 20849-20849/com.p9p.h20droidapp 
 D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.382 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.382 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.387 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.387 20849-20849/com.p9p.h20droidapp 
D/ForceDarkHelper: updateByCheckExcludeList: pkg: 
com.p9p.h20droidapp activity: com.p9p.h20droidapp.tab_view@936abe6
2023-02-24 10:40:12.419 20849-20849/com.p9p.h20droidapp 
D/ContentValues: likeTransaction:onComplete:null


from when incrementing using do transaction it reloads my previous activity

Python-telegram-bot not returning expected values

I've written a Python code to get a set of NFT prices from opensea's API and return it's total USD value below:

# Downgraded to python-telegram-bot 13.7 : pip install python-telegram-bot==13.7

# Set your bot token here.
bot_token = ''

# Import necessary libs
import os
import requests
from telegram.ext import Updater, CommandHandler, MessageHandler, filters

# ETH NFT portfolio contained in the dictionary.
eth_nfts_dict = {'pudgypenguins': 1,
                     'lilpudgys': 1,
                     'pudgyrods': 1}

# Set up logging
import logging
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)

# Setting up their polling stuff
updater = Updater(token=bot_token, use_context=True)
dispatcher = updater.dispatcher


## Functions

'''Fetch spot prices frm coinbase'''
def coinbase_spot(symbol):
    cb_url = f"https://api.coinbase.com/v2/exchange-rates?currency={symbol}"
    cb_response = requests.get(cb_url)
    return float(cb_response.json()['data']['rates']['USD'])


'''Print ETH NFT prices in portfolio, and total USD value'''
def get_eth_nfts(nfts_dict, name):

    eth_counter = 0

    for nft, qty in nfts_dict.items():
        url = f"https://api.opensea.io/api/v1/collection/{nft}/stats"
        headers = {"accept": "application/json"}
        response = requests.get(url, headers=headers)
        eth_floor_price = response.json()['stats']['floor_price']
        eth_counter += eth_floor_price * qty
        nfts_dict[nft] = eth_floor_price

    # Below code is to format the data to appear visually aligned on TG.
    # Wrap ``` to the string (for Markdown formatting)
    message = "```\n"

    for key, value in nfts_dict.items():
        message = message + "{0:<40} {1}".format(key, value) + "\n"

    message = message + f"\nTotal value of {name}'s ETH NFTs is {round(eth_counter, 3)} ETH ({'${:,.2f}'.format(eth_counter * coinbase_spot('eth'))})" + "\n```"

    return message

# Commands
def eth_nfts(update, context):
    context.bot.send_message(chat_id=update.effective_chat.id, text=get_eth_nfts(eth_nfts_dict, 'Ben'), parse_mode='Markdown') # parse_mode set to Markdown

# Create and add command handlers
eth_nfts_handler = CommandHandler('Ben_eth', eth_nfts)
dispatcher.add_handler(eth_nfts_handler)  # basically it's: updater.dispatcher.add_handler(CommandHandler('<command>', <command function>))

updater.start_polling()
updater.idle()  # make sure that program won't fail if 2 programs are attempted to run simultaneously.

The results in the image below shows what happens when I try to ping my Telegram bot: enter image description here

The intial '/ben_eth' command returns the correct Total value. But subsequent '/ben_eth' seem to be returning wrong Total values (multiples of the intial value). What is the bug here that is causing the subsequent Total values to be wrong?



from Python-telegram-bot not returning expected values

Friday 24 February 2023

Android Espresso Test case cancelled

I am executing the following espresso code. This code is working fine.

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.ActivityTestRule;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class EspressoTest {
    @Rule
    public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void view_isCorrect() {
        onView(withText("Cash less")).check(matches(isDisplayed()));
    }
}

But ActivityTestRule is deprecated. Instead of this I am trying to use ActivityScenarioRule. But the same code using of ActivityScenarioRule is showing Test Cancelled.

After modify the code will will look like following.

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class EspressoTest {
    @Rule
    public ActivityScenarioRule<MainActivity> activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);

    @Test
    public void view_isCorrect() {
        onView(withText("Cash less")).check(matches(isDisplayed()));
    }
}

I am getting following error: Error image

I am using the following dependency in Gradle.

androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'

And also included testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' in the default block.

I tried with updating/degrade the version. Tried another versions too. And also I tried the following Solution: Link

But no luck. Please help me to resolve this issue.

I am setting compileSdkVersion is 33.



from Android Espresso Test case cancelled

Pycord Voice Recieve: How to record silence at the beginning and end of a recording?

I used this example to record audio from a voice channel. Everything works, at the output I get a separate audio track for each speaking user.

Next, I want to overlay audio files on top of each other and glue the conversation in the channel into one file, but as a result, all the voices in the file sound almost simultaneously, although in fact the users speak in turn.

This is because py-cord does not record silence at the beginning and end of the user's audio recording.

Thus, if 3 people say something in turn, then as a result the recordings of their speech will have different lengths and because of this they are mixed into one heap.


How ​​can I record audio files of the same length for all users in a channel?


UPD:

This is a fragment of the recv_decoded_audio method from the discord.VoiceClient class. From here it can be understood that the silence between the user's speech is added by calculating the time between individual fragments of the user's speech.

def recv_decoded_audio(self, data):
    if data.ssrc not in self.user_timestamps:
        self.user_timestamps.update({data.ssrc: data.timestamp})
        # Add silence when they were not being recorded.
        silence = 0
    else:
        silence = data.timestamp - self.user_timestamps[data.ssrc] - 960
        self.user_timestamps[data.ssrc] = data.timestamp

    data.decoded_data = (
        struct.pack("<h", 0) * silence * opus._OpusStruct.CHANNELS
        + data.decoded_data
    )
       
    . . .

In this case, it makes sense that I can replace the line silence = 0 with this code:

. . .
self.start_recording_timestamp = int(datetime.now().timestamp())
. . .

silence = data.timestamp - self.start_recording_timestamp

And this will indeed add the user's silence at the beginning of the recording, but for some reason, instead of 5 seconds, a minute of silence is immediately added there.


How, in this case, is it correct to implement the addition of silence at the beginning of the recording, while not changing the library files?

How ​​to correctly calculate the silence time at the beginning of a recording?



from Pycord Voice Recieve: How to record silence at the beginning and end of a recording?

Codemirror v5 Dynamically Add Multiple Cursors

I'm using Codemirror v5.65.12. I know I can press and hold the CTRL key and then click to add multiple cursors into the editor. However mobile users don't have a CTRL key. My first attempt was using...

EventTarget.dispatchEvent() to trigger the ctrlKey to be down as long as a class is visible. I had no success with this attempt.

I'm also aware that Codemirror comes with an undocumented function triggerOnKeyDown with which you can trigger a keydown on Codemirror.

const ev = {
  type: 'keydown',
  keyCode: 13 // keycode for the Enter key, use any keycode here
};
cm.triggerOnKeyDown(ev);

However when I tried that using ctrlKey alongside onclick on the editor I wasn't able to successfully add multiple cursors for mobile in Codemirror.

I'm aware I can use .getCursor() to get the cursor's position and I can use .setCursor() to set the cursor's position but no matter what way I approach this I always manage to just move the cursor position to a new position instead of adding an additional cursor when I touch on mobile (specifically Android).

Is there a way in Codemirror v5.65.12 to dynamically add multiple cursors to a Codemirror instance that doesn't require a desktop (IE holding down the ctrlKey)? If not what other methods can I do to achieve this?

Codemirror .getCursor()



from Codemirror v5 Dynamically Add Multiple Cursors

Trouble parsing interview transcript (Q&As) where questioner name is sometimes redacted

I have the following python script I wrote as a reproducible example of my current pdf-parsing hangup. It:

  • downloads a pdf transcript from the web (Cassidy Hutchinson's 9/14/2022 interview transcript with the J6C)
  • reads/OCRs that pdf to text
  • attempts to split that text into the series of Q&A passages from the interview
  • runs a series of tests I wrote based on my manual read of the transcript

running the python code below generates the following output:

~/askliz  main !1 ?21  python stack_overflow_q_example.py                                                      ✔  docenv Py  22:41:00 
Test for passage0 passed.
Test for passage1 passed.
Test for passage7 passed.
Test for passage8 passed.
Traceback (most recent call last):
  File "/home/max/askliz/stack_overflow_q_example.py", line 91, in <module>
    assert nltk.edit_distance(passages[10][:len(actual_passage10_start)], actual_passage10_start) <= ACCEPTABLE_TEXT_DISCREPANCY, e_msg
AssertionError: Failed on passage 10

Your mission, should you choose to accept it: get this passage10 test to pass without breaking one of the previous tests. I'm hoping there's a clever regex or other modification in extract_q_a_locations below that will do the trick, but I'm open to any solution that passes all these tests, as I chose these test passages deliberately.

A little background on this transcript text, in case it's not as fun reading to you as it is to me: Sometimes a passage starts with a "Q" or "A", and sometimes it starts with a name (e.g. "Ms. Cheney."). The test that's failing, for passage 10, is where a question is asked by a staff member whose name is then redacted. The only way I've managed to get that test to pass has inadvertently broken one of the other tests, because not all redactions indicate the start of a question. (Note: in the pdf/ocr library I'm using, pdfplumber, redacted text usually shows up as just a bunch of extra spaces).

Code below:

import nltk
import re
import requests
import pdfplumber


def extract_q_a_locations(examination_text:str)->list:

    # (when parsed by pdfplumber) every Q/A starts with a newline, then spaces, 
    # then a line number and more spaces 
    prefix_regex = '\n\s+\d+\s+'

    # sometimes what comes next is a 'Q' or 'A' and more spaces
    qa_regex = '[QA]\s+'

    # other times what comes next is the name of a congressperson or lawyer for the witness
    speaker_regex = "(?:(?:Mr\.|Ms\.) \w+\.|-\s+)"

    # the combined regex I've been using is looking for the prefix then QA or Speaker regex
    pattern = f"{prefix_regex}(?:{speaker_regex}|{qa_regex})"
    delims = list(re.finditer(pattern, text))
    return delims

def get_q_a_passages(qa_delimiters, text):
    q_a_list = []
    for delim, next_delim in zip(qa_delimiters[:-1], qa_delimiters[1:]):
        # prefix is either 'Q', 'A', or the name of the speaker
        prefix = text[delim.span()[0]:delim.span()[1]].strip().split()[-1]

        # the text chunk is the actual dialogue text. everything from current delim to next one
        text_chunk = text[delim.span()[1]:next_delim.span()[0]]
        
        # now we want to remove some of the extra cruft from layout=True OCR in pdfplumber
        text_chunk = re.sub("\n\s+\d+\s+", " ", text_chunk)  # remove line numbers
        text_chunk = " ".join(text_chunk.split())            # remove extra whitespace
        
        q_a_list.append(f"{prefix} {text_chunk}")

    return q_a_list

if __name__ == "__main__":

    # download pdf
    PDF_URL = "https://www.govinfo.gov/content/pkg/GPO-J6-TRANSCRIPT-CTRL0000928888/pdf/GPO-J6-TRANSCRIPT-CTRL0000928888.pdf"
    FILENAME = "interview_transcript_stackoverflow.pdf"

    response = requests.get(PDF_URL)
    with open(FILENAME, "wb") as f:
        f.write(response.content)

    # read pdf as text
    with pdfplumber.open(FILENAME) as pdf:
        text = "".join([p.extract_text(layout=True) for p in pdf.pages])

    # I care about the Q&A transcript, which starts after the "EXAMINATION" header
    startidx = text.find("EXAMINATION")
    text = text[startidx:]

    # extract Q&A passages
    passage_locations = extract_q_a_locations(text)
    passages = get_q_a_passages(passage_locations, text)

    # TESTS
    ACCEPTABLE_TEXT_DISCREPANCY = 2

    # The tests below all pass already.
    actual_passage0_start = "Q So I do first want to bring up exhibit"
    assert nltk.edit_distance(passages[0][:len(actual_passage0_start)], actual_passage0_start) <= ACCEPTABLE_TEXT_DISCREPANCY
    print("Test for passage0 passed.")

    actual_passage1 = "A This is correct."
    assert nltk.edit_distance(passages[1][:len(actual_passage1)], actual_passage1) <= ACCEPTABLE_TEXT_DISCREPANCY
    print("Test for passage1 passed.")

    # (Note: for the next two passages/texts, prefix/questioner is captured as "Cheney" & 
    # "Jordan", not "Ms. Cheney" & "Mr. Jordan". I'm fine with either way.
    actual_passage7_start = "Cheney. And we also, just as" 
    assert nltk.edit_distance(passages[7][:len(actual_passage7_start)], actual_passage7_start) <= ACCEPTABLE_TEXT_DISCREPANCY
    print("Test for passage7 passed.")

    actual_passage8_start = "Jordan. They are pro bono"
    assert nltk.edit_distance(passages[8][:len(actual_passage8_start)], actual_passage8_start) <= ACCEPTABLE_TEXT_DISCREPANCY
    print("Test for passage8 passed.")

    # HERE'S MY PROBLEM. 
    # This test fails because my regex fails to capture the question which starts with the 
    # redacted name of the staff/questioner. The only way I've managed to get this test to 
    # pass has also broken at least one of the tests above. 
    actual_passage10_start = " So at this point, as we discussed earlier, I'm going to"
    e_msg = "Failed on passage 10"
    assert nltk.edit_distance(passages[10][:len(actual_passage10_start)], actual_passage10_start) <= ACCEPTABLE_TEXT_DISCREPANCY, e_msg


from Trouble parsing interview transcript (Q&As) where questioner name is sometimes redacted

Unexpected scrolling behavior in mobile Safari

I am trying to style a chatbot, but I am experiencing some unexpected scrolling, or lack their of, behavior in a div, but only in only in iOS/mobile Safari.

The expected behavior is to have the div scroll once a new element is added or made visible. This is the case in Chrome and desktop Safari. But in mobile safari it doesn't occur. Here is a screen shot of the problem:

does not scroll

Here's a live example, note this works perfectly on desktop Safari and Chrome - the issue is only occurring on iOS/Mobile Safari:

function randint(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}



function update_transcript(party, entry, time) {

        time = time.toLocaleString('en-US', {timeStyle: 'short'});

    if (party == "Human: "){
        document.getElementsByClassName("transcript")[0].innerHTML += `<div class="flex justify-end">
                            <div class="mt-3">
                                <div class="flex justify-end">
                                    <div class="bg-white shadow border border-gray-100 text-right rounded-l-lg rounded-tr-lg w-fit max-w-xs py-2 px-3">`
                                        +entry+
                                    `</div>
                                </div>
                                <div class="p-1 text-right text-xs text-gray-400">
                                    <span class="receipt font-semibold hidden">Read</span> <span>`+time+`</span>
                                </div>
                            </div>
                        </div>`;

    }else if (party == "BOT: "){
        document.getElementsByClassName("transcript")[0].innerHTML += `<div class="flex justify-start">
                            <div class="mt-3">
                                <div class="flex justify-start">
                                    <div class="bg-gray-100 text-left rounded-r-lg rounded-tl-lg shadow w-fit max-w-xs py-2 px-3">`
                                        +entry+
                                    `</div>
                                </div>
                                <div class="p-1 text-left text-xs text-gray-400">
                                    <span>`+time+`</span>
                                </div>
                            </div>
                        </div>`;

    }
}


    update_transcript('BOT: ', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum augue eu scelerisque finibus. Proin tincidunt elit a justo tincidunt porttitor. Mauris ac porttitor nulla. Cras hendrerit sem eu risus hendrerit, vel bibendum dolor congue. Pellentesque id efficitur leo. Nullam non elit nunc. Ut sit amet magna at purus sollicitudin lobortis. Aenean sed massa tortor. Integer lobortis dui massa, quis mollis elit porttitor at. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.?', new Date());



const query = document.getElementsByClassName("query")[0];

const form = document.querySelector('form');
form.addEventListener('submit', event => {
    event.preventDefault();

    document.getElementsByClassName("submit-query")[0].classList.add("hidden");


    //updates the chat window
    update_transcript('Human: ', query.value, new Date());

    // clear query text bos
    document.getElementsByClassName("query")[0].value = "";



var data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc elementum augue eu scelerisque finibus. Proin tincidunt elit a justo tincidunt porttitor. Mauris ac porttitor nulla. Cras hendrerit sem eu risus hendrerit, vel bibendum dolor congue. Pellentesque id efficitur leo. Nullam non elit nunc. Ut sit amet magna at purus sollicitudin lobortis. Aenean sed massa tortor. Integer lobortis dui massa, quis mollis elit porttitor at. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.";

        // adds a read receipt
        document.querySelectorAll('.receipt')[document.querySelectorAll('.receipt').length - 1].classList.remove("hidden");


        if (data) {

            // generated a random number between 1 and 4 seconds for a response time (when they start typing)
            let response_speed = randint(1000, 4000);

            // sets how long AI types for 50ms per character
            let typing_speed = (data.length * 40);


            setTimeout(function(){

                document.getElementsByClassName("typing-indicator")[0].classList.remove("hidden");

                setTimeout(function(){

                    // updates the chat window with the response
                    update_transcript('BOT: ', data, new Date());

                    document.getElementsByClassName("typing-indicator")[0].classList.add("hidden");

                    document.getElementsByClassName("error")[0].classList.add("hidden");

                }, typing_speed);

            }, response_speed);


        }


});
.typing-container{
    height: 10px;
    width: 40px;
}

.typing {
    position: relative;
}
.typing span {
    content: "";
    animation: blink 1.5s infinite;
    animation-fill-mode: both;
    height: 10px;
    width: 10px;
    background-color: rgb(156, 163, 175);
    position: absolute;
    left: 0;
    top: 0;
    border-radius: 50%;
}
.typing span:nth-child(2) {
    animation-delay: 0.2s;
    margin-left: 15px;
}
.typing span:nth-child(3) {
    animation-delay: 0.4s;
    margin-left: 30px;
}

@keyframes blink {
    0% {
        opacity: 0.1;
    }
    20% {
        opacity: 1;
    }
    100% {
        opacity: 0.1;
    }
}
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, shrink-to-fit=no" />
    <script src="https://cdn.tailwindcss.com"></script>
  
  <body class="bg-gray-100 px-3 pt-3">

    <div class="bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all h-96 w-96">

        <div class="flex flex-col justify-between h-full">




            <div class="flex flex-col-reverse overflow-y-scroll p-3 h-full">

                <div class="typing-indicator flex justify-start hidden">
                    <div class="bg-gray-100 rounded-lg p-3 mb-4">
                        <div class="typing-container">
                            <div class="typing">
                                <span></span>
                                <span></span>
                                <span></span>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="error flex justify-center p-3 hidden">
                    <p class="error-message text-red-600 text-sm text-center font-medium">We apologize for the inconvenience, our chat encountered an error. Please try again later.</p>
                </div>

                <div class="transcript"></div>

            </div>


            <div class="flex flex-col">
                <div class="relative border-t border-gray-100 p-1">
                    <form class="mb-0">
                        <input type="text" placeholder="Message..." maxlength="255" class="query bg-white outline-none w-full pl-6 pr-12 py-3"/>
                        <span class="absolute right-0 pr-3">
                            <button type="submit" class="submit-query pr-3 py-3 w-9 cursor-pointer">
                                <svg class="fill-current" viewBox="0 0 20 20">
                                    <path d="M17.218,2.268L2.477,8.388C2.13,8.535,2.164,9.05,2.542,9.134L9.33,10.67l1.535,6.787c0.083,0.377,0.602,0.415,0.745,0.065l6.123-14.74C17.866,2.46,17.539,2.134,17.218,2.268 M3.92,8.641l11.772-4.89L9.535,9.909L3.92,8.641z M11.358,16.078l-1.268-5.613l6.157-6.157L11.358,16.078z" />
                                </svg>
                            </button>
                        </span>
                    </form>
                </div>
            </div>





        </div>

    </div>



</body>

I need to have the div scroll to the bottom when new content is added or made visible. How can I fix this on mobile/iOS Safari?



from Unexpected scrolling behavior in mobile Safari

Parallelising a select query in Python's sqlite does not seem to improve performance

After reading this post, I have been trying to compare parallelization with non parallelization in sqlite. I am using Python's sqlite3 library to create a database containing a table called randNums which contains a two columns, an id and a val. The val is a random number between 0 and 1. I then select all rows with val greater than a half. I have done this twice so as to compare run times of the parallelized version and unparallelized version, however they take the same amount of time. I'd like to know if I am using the keyword 'PARALLEL' incorrectly, or if I need to first enable parallelization with a python command.

Finally, I'd also like to know how parallelization differs for different databases, for example, mysql and postgresql.

import sqlite3
from time import time

con = sqlite3.connect('mydatabase.db')
cursorObj = con.cursor()

cmd  = ['PRAGMA table_info(randNums);']
cmd += ['SELECT count (*) from randNums where val>.5;']
cmd += ['SELECT count /*+ PARALLEL(4) */ (*) from randNums where val>.5;']

for c in cmd:
    t = time()
    cursorObj.execute(c)
    print('command: %s' % c)
    print(cursorObj.fetchall())
    print('run time in seconds: %.3f\n\n' % (time()-t))

Running a Python script containing the above code results in the following output:

command: PRAGMA table_info(randNums);
[(0, 'id', 'INTEGER', 0, None, 1), (1, 'val', 'REAL', 0, None, 0)]
run time in seconds: 0.000


command: SELECT count (*) from randNums where val>.5;
[(49996009,)]
run time in seconds: 3.604


command: SELECT count /*+ PARALLEL(4) */ (*) from randNums where val>.5;
[(49996009,)]
run time in seconds: 3.598

I first generated the database with the following code:

import sqlite3
from random import uniform as rand

con = sqlite3.connect('mydatabase.db')
cursorObj = con.cursor()
cursorObj.execute("CREATE TABLE IF NOT EXISTS randNums(id integer PRIMARY KEY, val real)")
try:
    for i in range(10**8):
        if i%10**5==0: print(i)
        cursorObj.execute("INSERT INTO randNums VALUES(%d, '%f')" % (i,rand(0,1)))
except:
    print('datbase is already filled with data')
    pass
con.commit()


from Parallelising a select query in Python's sqlite does not seem to improve performance

Thursday 23 February 2023

How to view logs of oauth.js in Google example code

I am trying to follow the code in this Chrome Developers documentation and some things were going wrong so I thought I would add some console.log messages from my oauth.js.

I can see the log message from my foreground javascript directly in chrome by right-clicking on any page and selecting "inspect"... and I can view the logs of my background javascript by selecting "service worker" from the manage extensions page... but viewing logs from oauth.js ? - I have no idea how or where to view them.

I know some console.log() calls must have been made just by inspecting the error report below:

enter image description here



from How to view logs of oauth.js in Google example code

Using locally a tensorflow model from Google AutoML Tabular

I want to use my trained tensorflow model from Google VertexAI AutoML for tabular data locally but I don't understand the documentation and how to pass the input into my model to get the prediction into a backtesting module (so I don't want to deploy it yet). The model is stored in a directory under saved_model.pb alongside with 3 directories : variables, assets, assets.extra

Model was trained with:

tensorflow: "2.8.0"
struct2tensor: "0.39.0"
tensorflow-addons: "0.16.1"

Some more info i got from my python code:

model = tf.saved_model.load(path)
print(list(model.signatures.keys()))
infer = model.signatures["serving_default"]
print(infer.structured_outputs)

Output:

['serving_default']

{'classes': <tf.Tensor 'Reshape_25:0' shape=(None, None) dtype=string>, 'scores': <tf.Tensor 'truediv:0' shape=(None, 2) dtype=float32>}

I trained on a tabular data, a pandas DataFrame with 14 features and the target column. How can I pass my 14 column vector as input of my model ? I heard it might be possible also to repackage the saved_model.pb into a keras model, but the inference is less optimized, but I am ready to go that way.

So far I tried these references from official doc:

https://github.com/Lornatang/TensorFlow2-tutorials/blob/master/guide/serialization/saved_model.py

https://www.tensorflow.org/tutorials/keras/save_and_load

I didn't find any VertexAI documentation to deploy locally without a docker container to serve the model.



from Using locally a tensorflow model from Google AutoML Tabular

How to generate .exe of a kivymd application without console properly?

I'm trying to generating a .exe of my kivymd code. I have coded a really simple code, because i was trying to learn how to do this.

My code is as follows:

from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import Screen
 
class Demo(MDApp):
    def build(self):
        self.screen = Screen()
         
        self.l1 = MDLabel(
            text = "My Label"
        )

        self.screen.add_widget(self.l1)
        return self.screen 
 
if __name__ == "__main__":
    Demo().run()

Really simple. So i'm using a .spec like this:

# -*- mode: python ; coding: utf-8 -*-
import os
from kivy_deps import sdl2, glew
block_cipher = None
from kivymd import hooks_path as kivymd_hooks_path

current_path = os.path.abspath('.')
icon_path = os.path.join(current_path, 'imagens', 'icone.ico')

a = Analysis(['main.py'],
             pathex=[current_path],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[kivymd_hooks_path],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
          name='Eaton',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          icon=icon_path,
          console=False )

My code file name is main.py and my .spec is main.spec. So my problem is, when I use the console=True, this code and .spec works fine and create a good .exe, but when I use console=False the aplication returns error. If someone can help me I will be very thankful.



from How to generate .exe of a kivymd application without console properly?

Tkinter error: Couldn't recognize data in image file

I'm trying to put a jpg image to a tkinter canvas. tkinter gives me this error:

couldn't recognize data in image file

I use the code from the documentation:

canv = Canvas(root, width=80, height=80, bg='white')
canv.grid(row=2, column=3)

img = PhotoImage(file="bll.jpg")
canv.create_image(20,20, anchor=NW, image=img)

Same thing with png images. Even tried to put an image into a label widget, but got the same error. What's wrong?

I am using Python 3 on Mac. Python file and image are in the same folder.



from Tkinter error: Couldn't recognize data in image file

Wednesday 22 February 2023

convert bytes to bits with leading zeros

I know that i can do this :

byte = 58

format ( byte , '08b' )


>>> '00111010'

with two bytes i have to do

format( bytes , '016b')

but if i doesn't have the number of bytes i can't set a number for format so i have to do :

with open('file','rb')as a:
    b = a.read()
    c = int.from_bytes ( b )
    d = format( c ,'b')
d = (8-len(a)%8)*'0'+d

but i was wondering if there was easier way to do this and i want this without using any loops

thanks!



from convert bytes to bits with leading zeros

How to launch Google Autofill's "Scan New Card" intent programmatically

I want to launch the "Scan New Card" activity from my Android Application. This functionality is provided by Google to scan and fill the credit/debit card number inside the EditText and is the part of GMS Autofill Service.

My purpose is to provide a button to launch this intent, instead of using the existing option which shows up in the Autofill Popup/Ime Options.

I need to develop something like these screenshots from Google Play Store's Add Payment method and Google Autofill's Add Payment method.

Google Play Store's Add Payment method Google Autofill's Add Payment method Scan New Card IME option via Autofill
Google Play Store's Add Payment method Google Autofill's Add Payment method Scan New Card IME option via Autofill

What I've tried:

  1. I have tried the conventional way of adding the autofillHint="creditCardNumber" to the EditText and thus showing the option to launch using the autofill service, however, if, for some reason, the Autofill service is disabled or not set to "Google" then this autofill option of "Scan New Card" doesn't show up.

  2. I have tried to launch the intent directly using the Activity and Package name I found from the Logcat, however a Permission Denied: Activity not exported is thrown.

My Findings:

Logcat output when "Scan New Card" option provided by Google Autofill Service is clicked:

START u0 {act=SCAN_PAYMENT_CARD cmp=com.google.android.gms/.autofill.ui.AutofillActivity (has extras)} from uid 10102
START u0 {act=com.google.android.gms.ocr.ACTION_CREDIT_CARD_OCR pkg=com.google.android.gms cmp=com.google.android.gms/.ocr.SecuredCreditCardOcrActivity (has extras)} from uid 10102

Logcat output when launching the above intent directly using the Activity and Package name:

Permission Denial: starting Intent { act=SCAN_PAYMENT_CARD cmp=com.google.android.gms/.autofill.ui.AutofillActivity } from ProcessRecord{a45d996 13710:com.example.activity/u0a88} (pid=13710, uid=10088) not exported from uid 10013
FATAL EXCEPTION: main
Process: com.example.activity, PID: 13710
java.lang.SecurityException: Permission Denial: starting Intent { act=SCAN_PAYMENT_CARD cmp=com.google.android.gms/.autofill.ui.AutofillActivity } from ProcessRecord{a45d996 13710:com.example.activity/u0a88} (pid=13710, uid=10088) not exported from uid 10013

TLDR: I want to launch the following screen directly from a click of a button from my application instead of depending on the Google's Autofill Service.

Scan a Card Activity provided by Google's Autofill Service



from How to launch Google Autofill's "Scan New Card" intent programmatically

AWS Amplify Tutorial React Web App Doesn't Load in Safari

I am following the tutorial at https://aws.amazon.com/getting-started/hands-on/build-react-app-amplify-graphql/ to create a React single-page-application with AWS Amplify.

I have successfully completed the first two modules, and have observed an issue which I would like to resolve in the third module. After following most of the steps in Module Three, I am able to run the app locally - but opening localhost in Safari shows only a white page. In Chrome, localhost shows the expected log-in screen. From opening the console in Safari, I see an error:

"TypeError: n.addEventListener is not a function. (In 'n.addEventListener("change", o)', 'n.addEventListener' is undefined)"

which I simply have no idea how to debug. I think it is an issue in one of the node packages. I would like to resolve this, as it is my plan to amend the HTML and CSS portions of the codebase to host a simple website - which will need to work on the most common browsers.

I have shared a screenshot below.

enter image description here



from AWS Amplify Tutorial React Web App Doesn't Load in Safari

React Native Debugger Not Working on iOS simulator or actual iPhone

I used npm init -g <project>. The app uses a blank template.

I started the app using npm start.

I open the debugger in the iOS simulator by hitting j. However, the app instantly loses connection. This same thing happens when I use expo go on my actual iPhone. Does anyone know what is causing this?

enter image description here



from React Native Debugger Not Working on iOS simulator or actual iPhone

Stale Element - Selenium - Python

So I'll start by saying that this has became such a mess with me trying to solve this issue, other times I have been able to resolve the stale element issue.

Problem all starts after the first players stats are stored ( Everything it should be doing up this point ), and then once it goes back to loop and find the next player we have the issue.

I'm not sure if its caused by the nested loops or what. I try reinstating the variable that is giving me the issues I assume all throughout the code. player_stats

The thing is I did have it previously going through 5 players, and I am not sure what happened, or when the bug first established itself lol, as I was working on getting the rounds won, and played situated.

(We aren't even able to print("Found playerCol element") on the second go around)

All print statements works till it hangs in the while loop after the first iteration.

Here is the full code (with comments):

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions importStaleElementReferenceException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import pandas as pd
import re
import time

# Initialize the webdriver
driver = webdriver.Firefox()

# Navigate to the website
url = "https://www.hltv.org/stats/players"
driver.get(url)

WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.ID, "CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll"))).click()

# Find the elements containing the player statistics
player_stats = WebDriverWait(driver, 10).until(
    EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".playerCol, .statsDetail"))
)


# Extract the relevant data from the elements
players = []

for i, player_stat in enumerate(player_stats):
    try:
        WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
        while True:
            player_stats = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
            try:    
                if "playerCol" in player_stat.get_attribute("class"):
                    print("Found playerCol element")
                    name = player_stat.find_element(By.CSS_SELECTOR, "a").text if player_stat.find_elements(By.CSS_SELECTOR, "a") else player_stat.text
                    print(f"Name: {name}")
                elif "statsDetail" in player_stat.get_attribute("class"):
                    stats = player_stat.text.split()
                    if len(stats) >= 1 and re.search(r"\d+\.\d+", stats[0]):
                        kd_ratio = stats[0]
                break
            except StaleElementReferenceException as e:
                player_stats = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
                player_stats = driver.find_elements(By.CSS_SELECTOR, ".playerCol, .statsDetail")
                print(f"An error occurred while processing match stats: {e}")
                break

        # Extract the player stats
        if "statsDetail" in player_stat.get_attribute("class"):
            stats = player_stat.text.split()
            if len(stats) >= 1 and re.search(r"\d+\.\d+", stats[0]):
                kd_ratio = stats[0]

                # Process match stats for the player
                try:
                    time.sleep(1)
                    WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".playerCol, .statsDetail")))
                    player_link = driver.find_element(By.XPATH, f"//a[contains(text(), '{name}')]")
                    print(player_link.get_attribute('outerHTML'))
                    driver.execute_script("arguments[0].click();", player_link)
                    time.sleep(1)
                    player_stats = driver.find_elements(By.CSS_SELECTOR, ".playerCol, .statsDetail")
                    player = [name, kd_ratio]

                    # Extract additional player stats
                    headshot_percentage = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//span[contains(text(), 'Headshot %')]/following-sibling::span"))).text
                    player.append(headshot_percentage)

                    kpr = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//span[contains(text(), 'Kills / round')]/following-sibling::span"))).text
                    player.append(kpr)

                    dpr = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//span[contains(text(), 'Deaths / round')]/following-sibling::span"))).text
                    player.append(dpr)

                    # Extract match stats for the player
                    matches_link = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "a[href*='/stats/players/matches/'][data-link-tracking-destination='Click on Matches -> Individual -> Overview [subnavigation]']")))
                    driver.execute_script("arguments[0].click();", matches_link)
                    
                    match_stats = WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.group-2, tr.group-1")))
                    match_scores = []
                    num_of_matches = 0
                    rounds_won = 0
                    rounds_played = 0
                    # Process match stats for the player
                    for i, match_stat in enumerate(match_stats):
                        player_name = player[0]
                        player_team = driver.find_element(By.CSS_SELECTOR, ".gtSmartphone-only span:last-of-type").text
                        try:
                            team_name = ""
                            score = ""
                            while team_name == "" or score == "":
                                try:
                                    team = match_stat.find_element(By.CSS_SELECTOR, ".gtSmartphone-only span:last-of-type").text
                                    team_name = team.strip()
                                    
                                    score_span = match_stat.find_element(By.XPATH, ".//div[contains(@class, 'gtSmartphone-only')]//*[contains(text(), '(')]")
                                    score_text = score_span.text.strip()
                                
                                    score = re.search(r'\((\d+)\)', score_text).group(1)
                                    
                                except:
                                    time.sleep(1)
                                    match_stats = WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.group-2, tr.group-1")))
                                    match_stat = match_stats[i]
                            team_data = match_stat.find_elements(By.CSS_SELECTOR, ".gtSmartphone-only span")
                            print("Team data:", team_data[3].text)
                            if team_name.lower() == player_team.lower():
                                player_score = score
                                opposing_team_name = team_data[2].text.strip()
                                print(opposing_team_name)
                                opposing_team_score = team_data[3].text.strip('()')
                                print("Score strip: ", opposing_team_score)
                                rounds_won += int(player_score)
                                rounds_played += int(player_score) + int(opposing_team_score)
                            else:
                                player_score = team_data[1].text.strip('()')
                                print(player_score)
                                opposing_team_score = score
                                print(opposing_team_score)
                                opposing_team_name = team_data[0].text.strip()
                                print(opposing_team_name)
                                rounds_won += int(opposing_team_score)
                                rounds_played += int(player_score) + int(opposing_team_score)

                            match_scores.append((team_name, opposing_team_name, player_score, opposing_team_score))
                            num_of_matches += 1

                            if num_of_matches == 5: # exit loop after 5 iterations
                                break

                        except:
                            # Refresh the page if the element can't be found
                            driver.back()
                            player_stats = driver.find_elements(By.CSS_SELECTOR, ".playerCol, .statsDetail")
                            time.sleep(1)
                            match_stats = WebDriverWait(driver, 5).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.group-2, tr.group-1")))

                except Exception as e:
                    print(f"An error occurred while processing data for player {name}: {e}")
                    continue

                players.append([name, kd_ratio, headshot_percentage, kpr, dpr, rounds_won, rounds_played])
                print(players)
                print(f"{player_name}: {rounds_won} rounds won out of {rounds_played} rounds played in {num_of_matches} matches")
                driver.get(url)
                time.sleep(1)
    except StaleElementReferenceException as e:
    # handle the exception here
        print(f"An error occurred while processing match stats: {e}")
        break
# Close the webdriver
driver.quit()
# Store the data in a Pandas dataframe
df = pd.DataFrame(players, columns=["Name", "K/D", "HS %", "KPR", "DPR", "RW", "RP"])

# Clean the data
df["K/D"] = df["K/D"].str.extract(r"(\d+\.\d+)").astype(float)
df["HS %"] = df["HS %"].str.extract(r"(\d+\.\d+)").astype(float)
df["KPR"] = df["KPR"].str.extract(r"(\d+\.\d+)").astype(float)
df["DPR"] = df["DPR"].str.extract(r"(\d+\.\d+)").astype(float)



# Drop any rows that have missing or invalid data
df.dropna(subset=["Name", "K/D", "HS %", "KPR", "DPR"], inplace=True)


# Save the data to a CSV file
df.to_csv("player_stats.csv", index=False, sep='\t')

# Close the webdriver
driver.quit() 


from Stale Element - Selenium - Python