Sheldon  Grant

Sheldon Grant

1669709646

What The Return Keyword in JavaScript Does?

What does return do in JavaScript? The return keyword explained

The return keyword in JavaScript is a keyword to build a statement that ends JavaScript code execution. For example, suppose you have the following code that runs two console logs:

console.log("Hello");

return;

console.log("How are you?");

You’ll see that only “Hello” is printed to the console, while “How are you?" is not printed because of the return keyword. In short, the return statement will always stop any execution in the current JavaScript context, and return control to the higher context. When you use return inside of a function, the function will stop running and give back control to the caller.

The following function won’t print “Hi from function” to the console because the return statement is executed before the call to log the string. JavaScript will then give back control to the caller and continue code execution from there:

function fnExample() {
  return;
  console.log("Hi from function");
}

fnExample(); // nothing will be printed
console.log("JavaScript");
console.log("Continue to execute after return");

You can add an expression to the return statement so that instead of returning undefined, the value that you specify will be returned.

Take a look at the following code example, where the fnOne() function just return without any value, while fnTwo() returns a number 7:

function fnOne() {
  return;
}

function fnTwo() {
  return 7;
}

let one = fnOne();
let two = fnTwo();

console.log(one); // undefined
console.log(two); // 7

In the code above, the values returned from the return statement by each function are assigned to variables one and two. When return statement will give back undefined by default.

You can also assign a variable to the return statement as follows:

function fnExample() {
  let str = "Hello";
  return str;
}

let response = fnExample();
console.log(response); // Hello

And that’s all about the return keyword in JavaScript. You can combine the return statement and conditional if..else block to control what value returned from your function.

In the following example, the function will return “Banana” when number === 1 else it will return “Melon”:

function fnFruit(number) {
  if (number === 1) {
    return "Banana";
  } else {
    return "Melon";
  }
}

let fruit = fnFruit(1);
console.log(fruit); // "Banana"

Original article source at: https://sebhastian.com/

#javascript #return #keyword 

What The Return Keyword in JavaScript Does?
Monty  Boehm

Monty Boehm

1668615180

Making a Bash Script Return with Different Return Codes on Exit

Making a Bash Script Return with Different Return Codes on Exit

Exit codes are integer numbers that indicate that a script has been successfully executed. These codes are also known as return codes or exit statuses. Exit codes usually return zero upon successful execution and non-zero upon unsuccessful execution.

However, many Bash script users want to return with different return codes on exit, but they get errors. In this tutorial, we will explain the different approaches to make a Bash script return with different return codes on exit.

Bash Script Returns with Different Return Codes on Exit

Before moving out to the methods, let’s take a look at the exit codes that have specific meanings:

Exit CodesDescription
0The script is executed successfully.
1The script is executed with general errors.
2Invalid use of some built-in commands in the script.
126Shows the error for the command which is invoked and cannot be executed.
127The command doesn’t exist in the script.
128Shows the out-of-range exit code or fatal error signal.
130CTRL+C terminates the script.
255A general failure error code of the script.

How to Get Return Codes on Exit?

You only need to write the “echo $?” command to get the return code. For example, you want to compare two numbers using the following Bash script:

Once you execute the script in the terminal, run “echo $?” to get the return code on exit:

./comparison.sh

echo $?

The “comparison.sh” is executed successfully. That’s why terminals show zero as the return code. Similarly, you will get non-zero as the successful execution of the script. For example, if you use the Ls instead of the ls command in the script, you may get the non-zero as the return code:

As you can see in the previous image, the terminal shows 127 as the return code because the script contained the wrong command:

Make a Bash Script Return with Different Exit Codes

You can manually set up the exit codes in the script. For example, if you want to get 255 as the exit code, use the following script:

Now, execute the script and then run the “echo $?” command to get 255 as the return code:

./comparison.sh

echo $?

Conclusion

This is all about the exit codes that you may get after executing the Bash script in Linux. Exit codes help a user to identify the status of the Bash script. You can also manually set up and use the different return codes. Hence, you can get a non-zero exit code instead of zero even if the script is executed successfully. If you want to know more about the Bash scripts, browse our official website.

Original article source at: https://linuxhint.com/

#bash #script #return 

Making a Bash Script Return with Different Return Codes on Exit
Jammie  Yost

Jammie Yost

1661462400

Dockerfile Linter (written in Node.js)

Dockerfile Linter (written in Node.js)

Description

A Dockerfile linter that you can use to quickly check if your Dockerfile follows the best practices for building efficient Docker images. The tool uses ShellCheck analysis tool to lint code in RUN instructions. Some of the rules were inspired by Hadolint and Dockerfile reference documentation.

Installation

The linter requires Node.js to run:

npm install --global dockerlinter

Application

CLI

The linter can be used directly from the CLI:

dockerfilelinter -f <path to Dockerfile>
dockerfilelinter -f <path to Dockerfile> -s bash #default sh
dockerfilelinter -f <path to Dockerfile> -s none #disable shellcheck
dockerfilelinter -f <path to Dockerfile> -i ER0012,ER0015 #coma separated list of ignored rules
dockerfilelinter -f <path to Dockerfile> -e #return error code 1 for any error
dockerfilelinter -f <path to Dockerfile> -e warning #return error code 1 for errors with level 'warning' or higher(available levels: info, warning, error)

Docker

Docker allows you to run the linter on any type of platform. To mount the file, use the -v parameter:

docker build . -t imagename
docker run -v /tmp/files/dockerfile:/dockerfilelinter/dockerfile imagename linter -f dockerfile

Inline ignores

You can ignore rules for a specific instruction block in the Dockerfile by commenting it. The ignore comment must be applied above the instruction as # linter ignore=EF0003. The exceptions are ED and EL rules. Example:

# linter ignore=EF0003,EF0004
FROM node

YAML file with ignores

You can create YAML file "your dockerfile name".linter.yaml with list of ignored rules for specific Dockerfile or for all in folder dockerfilelinter.yaml. If you put file in the same folder where linting dockerfile is it will be auto-detected, but you can also use flag -y/--yaml to specify a path. Example:

ignored:
 - ER0012
 - ER0015

Rules

The list of rules implemented.

  • Rules with the E prefix come from dockerfilelinter. Implementation can be found in lib/lints.js.
  • Rules with the SC prefix come from ShellCheck. You can find their description in the tool's Wiki. For a more detailed description, use the Pages search tool.

Legend

ED - Error Directives EI - Error Instructions ER - Error RUN EC - Error COPY EU - Error USER EF - Error FROM EW - Error WORKDIR EE - Error EXPORT EL - Error Lines EA - Error ADD EJ - Error JSON

RulesDescription
EL0001Invalid line
ED0001All parser directives must be at the very top of a Dockerfile.
ED0002Directive appears more then once.
ED0003Directives should be lowercase.
ED0004Parser directive will be treated as a comment.
ED0005Missing value for directive.
ER0001Set the SHELL option -o (-eo for Alpine image) pipefail before RUN with a pipe in.
EU0001Last user should not be root.
EI0001There can only be one instruction like (CMD, HEALTHCHECK, ENTRYPOINT).
EI0002FROM may only be preceded by one or more ARG.
EF0001Missing FROM.
EC0001COPY --from cannot reference its own FROM alias.
EC0002COPY --from should reference a previously defined FROM alias.
EI0003MAINTAINER is deprecated, instead use LABEL.
EJ0001You must use double-quotes (") in JSON array.
EJ0002CMD and ENTRYPOINT should be written in JSON form.
EJ0003SHELL must be written in JSON form.
EF0002FROM aliases must be unique.
EF0003Using latest is prone to errors if the image will ever update.
EF0004Always tag the version of an image explicitly.
ER0002Delete the apt-get lists after installing something.
ER0003Use WORKDIR to switch to a directory.
ER0004Do not use sudo, consider using gosu.
ER0005Command (ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig) does not make sense in a container.
ER0006Using (apt-get upgrade, dist-upgrade, apk upgrade, apt install) is not recommended.
EA0001Use curl or wget instead, and delete files when no longer needed.
EC0003Use ADD for extracting archives into a image.
ER0007Either use wget or curl, but not both.
ER0008Use SHELL to change the default shell.
ER0009Use the -y switch.
ER0010Avoid additional packages by specifying --no-install-recommends.
EA0002Use COPY instead of ADD for files and folders.
EC0004COPY with more then 2 arguments requires the last argument to end with /.
ER0011Use the --no-cache switch.
ER0012Pin versions in apt get install.
ER0013Pin versions in pip install.
ER0014Pin versions in npm install.
ER0015Pin versions in apk add.
ER0016Pin versions in gem install.
EI0004Don't use (ONBUILD,FROM,MAINTAINTER) in ONBUILD.
EW0001Use absolute WORKDIR.
EE0001Valid UNIX ports range from 0 to 65535.
EI0005Instructions should be uppercase.

Development

You can help us develop linter by suggesting new rules and reporting bugs.

Tests

To run unit tests, use the command below:

npm run test

Maintainer

Docker Linter was created and developed by Buddy, creators of delivery automation tools for web and software developers.

CI/CD

The linter was created to validate the Dockerfile syntax in Continuous Integration and Delivery processes and can be used in any CI/CE tool. Buddy natively supports the linter, allowing you to create a pipeline that will check the syntax, build the Docker image and push it to the registry in a few simple steps:

  1. First, define when and for what refs (branchs / tags / pull requests) should the pipeline execute:

Docker Linter

  1. Next, add the Lint Dockerfile action and select the Dockerfile to validate:

Docker Linter

  1. Last, add the Docker build action that will build the image and push it to the registry.

Docker Linter

  1. You can extend the pipeline to automate other processes in your workflow. For example, automatically update the image on your K8s cluster and notify the QA team in case something goes wrong:

Docker Linter


Download Details:

Author: buddy-works
Source code: https://github.com/buddy-works/dockerfile-linter

License: GPL-3.0 license
#docker #nodejs 

Dockerfile Linter (written in Node.js)
Josefa  Corwin

Josefa Corwin

1659736920

Mailboxer: A Rails Gem to Send Messages inside A Web Application

Mailboxer

This project is based on the need for a private message system for ging / social_stream. Instead of creating our core message system heavily dependent on our development, we are trying to implement a generic and potent messaging gem.

After looking for a good gem to use we noticed the lack of messaging gems and functionality in them. Mailboxer tries to fill this void delivering a powerful and flexible message system. It supports the use of conversations with two or more participants, sending notifications to recipients (intended to be used as system notifications “Your picture has new comments”, “John Doe has updated his document”, etc.), and emailing the messageable model (if configured to do so). It has a complete implementation of a Mailbox object for each messageable with inbox, sentbox and trash.

The gem is constantly growing and improving its functionality. As it is used with our parallel development ging / social_stream we are finding and fixing bugs continously. If you want some functionality not supported yet or marked as TODO, you can create an issue to ask for it. It will be great feedback for us, and we will know what you may find useful in the gem.

Mailboxer was born from the great, but outdated, code from lpsergi / acts_as_messageable.

We are now working to make exhaustive documentation and some wiki pages in order to make it even easier to use the gem to its full potential. Please, give us some time if you find something missing or ask for it. You can also find us on the Gitter room for this repo. Join us there to talk.

Installation

Add to your Gemfile:

gem 'mailboxer'

Then run:

$ bundle install

Run install script:

$ rails g mailboxer:install

And don't forget to migrate your database:

$ rake db:migrate

You can also generate email views:

$ rails g mailboxer:views

Upgrading

If upgrading from 0.11.0 to 0.12.0, run the following generators:

$ rails generate mailboxer:namespacing_compatibility
$ rails generate mailboxer:install -s

Then, migrate your database:

$ rake db:migrate

Requirements & Settings

Emails

We are now adding support for sending emails when a Notification or a Message is sent to one or more recipients. You should modify the mailboxer initializer (/config/initializer/mailboxer.rb) to edit these settings:

Mailboxer.setup do |config|
  #Enables or disables email sending for Notifications and Messages
  config.uses_emails = true
  #Configures the default `from` address for the email sent for Messages and Notifications of Mailboxer
  config.default_from = "no-reply@dit.upm.es"
  ...
end

You can change the way in which emails are delivered by specifying a custom implementation of notification and message mailers:

Mailboxer.setup do |config|
  config.notification_mailer = CustomNotificationMailer
  config.message_mailer = CustomMessageMailer
  ...
end

If you have subclassed the Mailboxer::Notification class, you can specify the mailers using a member method:

class NewDocumentNotification < Mailboxer::Notification
  def mailer_class
    NewDocumentNotificationMailer
  end
end

class NewCommentNotification < Mailboxer::Notification
  def mailer_class
    NewDocumentNotificationMailer
  end
end

Otherwise, the mailer class will be determined by appending 'Mailer' to the mailable class name.

User identities

Users must have an identity defined by a name and an email. We must ensure that Messageable models have some specific methods. These methods are:

#Returning any kind of identification you want for the model
def name
  return "You should add method :name in your Messageable model"
end
#Returning the email address of the model if an email should be sent for this object (Message or Notification).
#If no mail has to be sent, return nil.
def mailboxer_email(object)
  #Check if an email should be sent for that object
  #if true
  return "define_email@on_your.model"
  #if false
  #return nil
end

These names are explicit enough to avoid colliding with other methods, but as long as you need to change them you can do it by using mailboxer initializer (/config/initializer/mailboxer.rb). Just add or uncomment the following lines:

Mailboxer.setup do |config|
  # ...
  #Configures the methods needed by mailboxer
  config.email_method = :mailboxer_email
  config.name_method = :name
  config.notify_method = :notify
  # ...
end

You may change whatever you want or need. For example:

config.email_method = :notification_email
config.name_method = :display_name
config.notify_method = :notify_mailboxer

Will use the method notification_email(object) instead of mailboxer_email(object), display_name for name and notify_mailboxer for notify.

Using default or custom method names, if your model doesn't implement them, Mailboxer will use dummy methods so as to notify you of missing methods rather than crashing.

Preparing your models

In your model:

class User < ActiveRecord::Base
  acts_as_messageable
end

You are not limited to the User model. You can use Mailboxer in any other model and use it in several different models. If you have ducks and cylons in your application and you want to exchange messages as if they were the same, just add acts_as_messageable to each one and you will be able to send duck-duck, duck-cylon, cylon-duck and cylon-cylon messages. Of course, you can extend it for as many classes as you need.

Example:

class Duck < ActiveRecord::Base
  acts_as_messageable
end
class Cylon < ActiveRecord::Base
  acts_as_messageable
end

Mailboxer API

Warning for version 0.8.0

Version 0.8.0 sees Messageable#read and Messageable#unread renamed to mark_as_(un)read, and Receipt#read and Receipt#unread to is_(un)read. This may break existing applications, but read is a reserved name for Active Record, and the best pratice in this case is simply avoid using it.

How can I send a message?

#alfa wants to send a message to beta
alfa.send_message(beta, "Body", "subject")

How can I read the messages of a conversation?

As a messageable, what you receive are receipts, which are associated with the message itself. You should retrieve your receipts for the conversation and get the message associated with them.

This is done this way because receipts save the information about the relation between messageable and the messages: is it read?, is it trashed?, etc.

#alfa gets the last conversation (chronologically, the first in the inbox)
conversation = alfa.mailbox.inbox.first

#alfa gets it receipts chronologically ordered.
receipts = conversation.receipts_for alfa

#using the receipts (i.e. in the view)
receipts.each do |receipt|
  ...
  message = receipt.message
  read = receipt.is_unread? #or message.is_unread?(alfa)
  ...
end

How can I reply to a message?

#alfa wants to reply to all in a conversation
#using a receipt
alfa.reply_to_all(receipt, "Reply body")

#using a conversation
alfa.reply_to_conversation(conversation, "Reply body")
#alfa wants to reply to the sender of a message (and ONLY the sender)
#using a receipt
alfa.reply_to_sender(receipt, "Reply body")

How can I delete a message from trash?

#delete conversations forever for one receipt (still in database)
receipt.mark_as_deleted

#you can mark conversation as deleted for one participant
conversation.mark_as_deleted participant

#Mark the object as deleted for messageable
#Object can be:
  #* A Receipt
  #* A Conversation
  #* A Notification
  #* A Message
  #* An array with any of them
alfa.mark_as_deleted conversation

# get available message for specific user
conversation.messages_for(alfa)

How can I retrieve my conversations?

#alfa wants to retrieve all his conversations
alfa.mailbox.conversations

#A wants to retrieve his inbox
alfa.mailbox.inbox

#A wants to retrieve his sent conversations
alfa.mailbox.sentbox

#alfa wants to retrieve his trashed conversations
alfa.mailbox.trash

How can I paginate conversations?

You can use Kaminari to paginate the conversations as normal. Please, make sure you use the last version as mailboxer uses select('DISTINCT conversations.*') which was not respected before Kaminari 0.12.4 according to its changelog. Working correctly on Kaminari 0.13.0.

#Paginating all conversations using :page parameter and 9 per page
conversations = alfa.mailbox.conversations.page(params[:page]).per(9)

#Paginating received conversations using :page parameter and 9 per page
conversations = alfa.mailbox.inbox.page(params[:page]).per(9)

#Paginating sent conversations using :page parameter and 9 per page
conversations = alfa.mailbox.sentbox.page(params[:page]).per(9)

#Paginating trashed conversations using :page parameter and 9 per page
conversations = alfa.mailbox.trash.page(params[:page]).per(9)

You can take a look at the full documentation for Mailboxer in rubydoc.info.

Do you want to test Mailboxer?

Thanks to Roman Kushnir (@RKushnir) you can test Mailboxer with this sample app.

I need a GUI!

If you need a GUI you should take a look at these links:

Contributors


Author: mailboxer
Source code: https://github.com/mailboxer/mailboxer
License: MIT license

#ruby  #ruby-on-rails 

Mailboxer: A Rails Gem to Send Messages inside A Web Application
Saul  Alaniz

Saul Alaniz

1654310400

Cree Una Aplicación GraphQL En Node.js Con TypeScript Y Graphql-reques

En este artículo, creará una aplicación de pila completa utilizando GraphQL y Node.js en el backend. Mientras tanto, nuestro frontend usará la graphql-requestbiblioteca para realizar operaciones de red en nuestro backend.

¿Por qué usar graphql-request y TypeScript?

Cada vez que los desarrolladores construyen un servidor GraphQL usando Apollo, la biblioteca genera una "interfaz" que se ve así:

Esta interfaz permite a los usuarios realizar consultas o solicitudes de mutación al servidor a través de un código. Sin embargo, hablemos del elefante en la habitación: no parece muy fácil de usar. Dado que la interfaz no presenta ningún botón ni ningún elemento de interfaz útil, puede ser difícil para muchos usuarios navegar por su aplicación. En consecuencia, esto reduce su base de usuarios. Entonces, ¿cómo resolvemos este problema?

Aquí es donde graphql-requestentra en juego. Es una biblioteca de código abierto que permite a los usuarios realizar consultas en un servidor GraphQL. Cuenta con las siguientes características:

  • Ligero: esta biblioteca tiene un poco más de 21 kilobytes minimizados, lo que garantiza que su aplicación se mantenga en funcionamiento
  • API basada en promesas: esto brinda soporte para aplicaciones asíncronas
  • Compatibilidad con TypeScript: graphql-requestes una de las muchas bibliotecas que permite TypeScript. Una de las principales ventajas de Typescript es que permite un código estable y predecible.

Por ejemplo, mira el siguiente programa:

let myNumber = 9; //here, myNumber is an integer
myNumber = 'hello'; //now it is a string.
myNumber = myNumber + 10; //even though we are adding a string to an integer,
//JavaScript won't return an error. In the real world, it might bring unexpected outputs.
//However, in Typescript, we can tell the compiler..
//what data types we need to choose.
let myNumber:number = 39; //tell TS that we want to declare an integer.
myNumber = 9+'hello'; //returns an error. Therefore, it's easier to debug the program
//this promises stability and security. 

En este artículo, crearemos una aplicación de pila completa utilizando GraphQL y TypeScript. Aquí, usaremos el apollo-server-expresspaquete para construir un servidor backend. Además, para la interfaz, usaremos Next y graphql-requestconsumiremos nuestra API GraphQL.

Construyendo nuestro servidor

Inicialización del proyecto

Para inicializar un proyecto Node.js en blanco, ejecute estos comandos de terminal:

mkdir graphql-ts-tutorial #create project folder 
cd graphql-ts-tutorial 
npm init -y #initialize the app

Cuando termine, ahora tenemos que decirle a Node que necesitamos usar TypeScript en nuestra base de código:

#configure our Typescript:
npx tsc --init --rootDir app --outDir dist --esModuleInterop --resolveJsonModule --lib es6 --module commonjs --allowJs true --noImplicitAny true
mkdir app #our main code folder
mkdir dist #Typescript will use this folder to compile our program.

A continuación, instale estas dependencias:

#development dependencies. Will tell Node that we will use Typescript
npm install -d ts-node @types/node typescript @types/express nodemon
#Installing Apollo Server and its associated modules. Will help us build our GraphQL
#server
npm install apollo-server-express apollo-server-core express graphql

Después de este paso, navegue a su appcarpeta. Aquí, crea los siguientes archivos:

  • index.ts: Nuestro archivo principal. Esto ejecutará y ejecutará nuestro servidor Express GraphQL
  • dataset.ts: Esto servirá como nuestra base de datos, que se servirá al cliente
  • Resolvers.ts: Este módulo manejará los comandos del usuario. Aprenderemos sobre los resolutores más adelante en este artículo.
  • Schema.ts: como sugiere el nombre, este archivo almacenará los esquemas necesarios para enviar datos al cliente

Al final, la estructura de carpetas debería verse así:

Creando nuestra base de datos

En esta sección, crearemos una base de datos ficticia que se utilizará para enviar los datos solicitados. Para hacerlo, vaya a app/dataset.tsy escriba el siguiente código:

let people: { id: number; name: string }[] = [
  { id: 1, name: "Cassie" },
  { id: 2, name: "Rue" },
  { id: 3, name: "Lexi" },
];
export default people;
  • Primero, creamos una matriz de objetos llamadapeople
  • Esta matriz tendrá dos campos: idde tipo numbery namede tipostring

Definiendo nuestro esquema

Aquí, ahora crearemos un esquema para nuestro servidor GraphQL.

En pocas palabras, un esquema de GraphQL es una descripción del conjunto de datos que los clientes pueden solicitar desde una API. Este concepto es similar al de la biblioteca Mongoose .
Para crear un esquema, vaya al app/Schema.tsarchivo. Allí escribe el siguiente código:

import { gql } from "apollo-server-express"; //will create a schema
const Schema = gql`
  type Person {
    id: ID!
    name: String
  }
  #handle user commands
  type Query {
    getAllPeople: [Person] #will return multiple Person instances
    getPerson(id: Int): Person #has an argument of 'id` of type Integer.
  }
`;
export default Schema; 
//export this Schema so we can use it in our project

Desglosemos este código pieza por pieza:

  • La Schemavariable contiene nuestro esquema GraphQL
  • Primero, creamos un Personesquema. Tendrá dos campos: idde tipo IDy namede tipoString
  • Más adelante, le indicamos a GraphQL que si el cliente ejecuta el getAllPeoplecomando, el servidor devolverá una matriz de Personobjetos
  • Además, si el usuario usa el getPersoncomando, GraphQL devolverá una sola Personinstancia

Creando resolutores

Ahora que hemos codificado nuestro esquema, nuestro siguiente paso es definir nuestros resolutores.
En términos simples, un resolver es un grupo de funciones que generan una respuesta para una consulta de GraphQL. En otras palabras, un resolver sirve como un controlador de consultas GraphQL.
En Resolvers.ts, escribe el siguiente código:

import people from "./dataset"; //get all of the available data from our database.
const Resolvers = {
  Query: {
    getAllPeople: () => people, //if the user runs the getAllPeople command
    //if the user runs the getPerson command:
    getPerson: (_: any, args: any) => { 
      console.log(args);
      //get the object that contains the specified ID.
      return people.find((person) => person.id === args.id);
    },
  },
};
export default Resolvers;
  • Aquí, creamos un Queryobjeto que maneja todas las consultas entrantes que van al servidor
  • Si el usuario ejecuta el getAllPeoplecomando, el programa devolverá todos los objetos presentes en nuestra base de datos
  • Además, el getPersoncomando requiere un argumento id. Esto devolverá una Personinstancia con el ID coincidente
  • Al final, exportamos nuestro resolver para que pudiera vincularse con nuestra aplicación.

Configurando nuestro servidor

¡Ya casi hemos terminado! Ahora que hemos creado tanto nuestro esquema como nuestro resolutor, nuestro siguiente paso es vincularlos.

En index.js, escribe este bloque de código:

import { ApolloServer } from "apollo-server-express";
import Schema from "./Schema";
import Resolvers from "./Resolvers";
import express from "express";
import { ApolloServerPluginDrainHttpServer } from "apollo-server-core";
import http from "http";

async function startApolloServer(schema: any, resolvers: any) {
  const app = express();
  const httpServer = http.createServer(app);
  const server = new ApolloServer({
    typeDefs: schema,
    resolvers,
    //tell Express to attach GraphQL functionality to the server
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
  }) as any;
  await server.start(); //start the GraphQL server.
  server.applyMiddleware({ app });
  await new Promise<void>((resolve) =>
    httpServer.listen({ port: 4000 }, resolve) //run the server on port 4000
  );
  console.log(`Server ready at http://localhost:4000${server.graphqlPath}`);
}
//in the end, run the server and pass in our Schema and Resolver.
startApolloServer(Schema, Resolvers);

¡Vamos a probarlo! Para ejecutar el código, use este comando Bash:

npx nodemon app/index.ts 

Esto creará un servidor en la localhost:4000/graphqlURL.

Aquí puede ver sus esquemas disponibles dentro de la interfaz de usuario:

¡Esto significa que nuestro código funciona!

Todas nuestras consultas de GraphQL irán dentro del panel de Operación . Para verlo en acción, escriba este fragmento dentro de este cuadro:

#make a query:
query {
  #get all of the people available in the server
  getAllPeople {
    #procure their IDs and names.
    id
    name
  }
}

Para ver el resultado, haga clic en el botón Ejecutar :

Incluso podemos buscar una entidad específica a través de la getPersonconsulta:

query ($getPersonId: Int) { #the argument will be of type Integer
  getPerson(id: 1) {
    #get the person with the ID of 1
    name
    id
  }
}

Creando mutaciones

En el mundo de GraphQL, las mutaciones son comandos que tienen efectos secundarios en la base de datos. Ejemplos comunes de esto incluyen:

  • Agregar un usuario a la base de datos: cuando un cliente se registra en un sitio web, el usuario realiza una mutación para guardar sus datos en su base de datos
  • Editar o eliminar un objeto: si un usuario modifica o elimina datos de una base de datos, esencialmente está creando una mutación en el servidor.

Para manejar mutaciones, vaya a su Schema.tsmódulo. Aquí, dentro de la Schemavariable, agregue las siguientes líneas de código:

const Schema = gql`
  #other code..
  type Mutation {
    #the addPerson commmand will accept an argument of type String.
    #it will return a 'Person' instance. 
    addPerson(name: String): Person
  }
`;

Nuestro próximo paso es crear un resolver para manejar esta mutación. Para hacerlo, dentro del Resolvers.tsarchivo, agregue este bloque de código:

const Resolvers = {
  Query: {
    //..further code..
  },
  //code to add:
  //all our mutations go here.
  Mutation: {
    //create our mutation:
    addPerson: (_: any, args: any) => {
      const newPerson = {
        id: people.length + 1, //id field
        name: args.name, //name field
      };
      people.push(newPerson);
      return newPerson; //return the new object's result
    },
  },
};
  • La addPersonmutación acepta un nameargumento.
  • Cuando namese pasa a, el programa creará un nuevo objeto con una nameclave coincidente
  • A continuación, utilizará el pushmétodo para agregar este objeto al conjunto de peopledatos .
  • Finalmente, devolverá las propiedades del nuevo objeto al cliente.

¡Eso es todo! Para probarlo, ejecute este código dentro de la ventana Operaciones :

#perform a mutation on the server
mutation($name: String) {
  addPerson(name:"Hussain") { #add a new person with the name "Hussain"
    #if the execution succeeds, return its 'id' and 'name` to the user.
    id
    name
  }
}

Verifiquemos si GraphQL ha agregado la nueva entrada a la base de datos:

query {
  getAllPeople { #get all the results within the 'people' database. 
  #return only their names
  name 
  }
}

Construyendo nuestro cliente

Hemos construido con éxito nuestro servidor. En esta sección, crearemos una aplicación cliente usando Next que escuchará al servidor y procesará datos en la interfaz de usuario.

Como primer paso, inicialice una aplicación Next.js en blanco así:

npx create-next-app@latest graphql-client --ts
touch constants.tsx #our query variables go here.

Para realizar operaciones GraphQL, utilizaremos la biblioteca graphql-request . Este es un módulo mínimo y de código abierto que nos ayudará a realizar mutaciones y consultas en nuestro servidor:

npm install graphql-request graphql
npm install react-hook-form #to capture user input

Creación de variables de consulta

En esta sección, codificaremos nuestras consultas y mutaciones para ayudarnos a realizar operaciones GraphQL. Para hacerlo, vaya a constants.tsxy agregue el siguiente código:

import { gql } from "graphql-request";
//create our query
const getAllPeopleQuery = gql`
  query {
    getAllPeople { #run the getAllPeople command
      id
      name
    }
  }
`;
//Next, declare a mutation
const addPersonMutation = gql`
  mutation addPeople($name: String!) {
    addPerson(name: $name) { #add a new entry. Argument will be 'name'
      id
      name
    }
  }
`;
export { getAllPeopleQuery, addPersonMutation };
  • En la primera parte, creamos la getAllPeopleQueryvariable. Cuando el usuario ejecuta esta consulta, el programa le indicará al servidor que obtenga todas las entradas presentes en la base de datos.
  • Más tarde, la addPersonmutación le dice a GraphQL que agregue una nueva entrada con su namecampo respectivo
  • Al final, usamos la exportpalabra clave para vincular nuestras variables con el resto del proyecto.

Realización de consultas

En pages/index.ts, escribe el siguiente código:

import type { NextPage, GetStaticProps, InferGetStaticPropsType } from "next";
import { request } from "graphql-request"; //allows us to perform a request on our server
import { getAllPeopleQuery } from "../constants"; 
import Link from "next/link";
const Home: NextPage = ({
  result, //extract the 'result' prop 
}: InferGetStaticPropsType<typeof getStaticProps>) => {
  return (
    <div className={styles.container}>
      {result.map((item: any) => { //render the 'result' array to the UI 
        return <p key={item.id}>{item.name}</p>;
      })}
    <Link href="/addpage">Add a new entry </Link>
    </div>
  );
};
//fetch data from the server
export const getStaticProps: GetStaticProps = async () => {
  //the first argument is the URL of our GraphQL server
  const res = await request("http://localhost:4000/graphql", getAllPeopleQuery);
  const result = res.getAllPeople;
  return {
    props: {
      result,
    }, // will be passed to the page component as props
  };
};
export default Home;

Aquí hay un desglose de este código pieza por pieza:

  • En el getStaticPropsmétodo, le indicamos a Next que ejecute el getAllPeoplecomando en nuestro servidor GraphQL
  • Posteriormente, devolvimos su respuesta al Homecomponente funcional. Esto significa que ahora podemos mostrar el resultado en la interfaz de usuario.
  • A continuación, el programa usó el mapmétodo para representar todos los resultados del getAllPeoplecomando en la interfaz de usuario. Cada elemento de párrafo mostrará los namecampos de cada entrada
  • Además, también usamos un Linkcomponente para redirigir al usuario a la addpageruta. Esto permitirá al usuario agregar una nueva Personinstancia a la tabla .

Para probar el código, ejecute el siguiente comando de terminal:

npm run dev

Este será el resultado:

Nuestro servidor GraphQL incluso se actualiza en tiempo real.

Realizando mutaciones

Ahora que hemos realizado con éxito una consulta, incluso podemos realizar mutaciones a través de la graphql-requestbiblioteca.

Dentro de su pagescarpeta, cree un nuevo archivo llamado addpage.tsx. Como sugiere el nombre, este componente permitirá al usuario agregar una nueva entrada a la base de datos. Aquí, comience escribiendo el siguiente bloque de código:

import type { NextPage, GetStaticProps, InferGetStaticPropsType } from "next";
import { request } from "graphql-request";
import { addPersonMutation } from "../constants";
const AddPage: NextPage = () => {
  return (
    <div>
      <p>We will add a new entry here. </p>
    </div>
  );
};
export default AddPage;

En este fragmento de código, estamos creando una página en blanco con un fragmento de texto. Estamos haciendo esto para asegurarnos de que nuestro sistema de enrutamiento de URL funcione.

¡Esto significa que usamos el enrutamiento con éxito! A continuación, escribe este fragmento en tu addpage.tsxarchivo:

import { useForm } from "react-hook-form";
const { register, handleSubmit } = useForm();
//if the user submits the form, then the program will output the value of their input.
const onSubmit = (data: any) => console.log(data);
return (
  <div>
    <form onSubmit={handleSubmit(onSubmit)}> {/*Bind our handler to this form.*/}
      {/* The user's input will be saved within the 'name' property */}
      <input defaultValue="test" {...register("name")} />
      <input type="submit" />
    </form>
  </div>
);

Esta será la salida:

 

Ahora que hemos capturado con éxito la entrada del usuario, nuestro último paso es agregar su entrada al servidor.

Para hacerlo, cambie el onSubmitcontrolador ubicado en pages/addpage.tsxel archivo de esta manera:

const onSubmit = async (data: any) => {
  const response = await request(
    "http://localhost:4000/graphql",
    addPersonMutation,
    data
  );
  console.log(response);
};
  • Aquí, estamos realizando una solicitud de mutación a nuestro servidor GraphQL a través de la requestfunción
  • Además, también pasamos el addPersoncomando de mutación a nuestro encabezado de solicitud. Esto le indicará a GraphQL que realice la addMutationacción en nuestro servidor

Este será el resultado:

¡Y hemos terminado!

Conclusión

Aquí está el código fuente completo de este proyecto.

En este artículo, aprendió a crear una aplicación completa con GraphQL y TypeScript. Ambas son habilidades extremadamente cruciales dentro del mundo de la programación, ya que tienen una gran demanda en la actualidad.

Si encontró alguna dificultad en este código, le aconsejo que deconstruya el código y juegue con él para que pueda comprender completamente este concepto.

Muchas Gracias Por Leer! ¡Feliz codificación!

Esta historia se publicó originalmente en https://blog.logrocket.com/build-graphql-app-node-js-typescript-graphql-request/

#graphql #typescript #nodejs 

Cree Una Aplicación GraphQL En Node.js Con TypeScript Y Graphql-reques
坂本  篤司

坂本 篤司

1654310040

TypeScriptとgraphql-requestを使用してNode.jsでGraphQLアプリを構築します

この記事では、バックエンドでGraphQLとNode.jsを使用してフルスタックアプリを構築します。一方、フロントエンドはgraphql-requestライブラリを使用してバックエンドでネットワーク操作を実行します。

なぜgraphql-requestとTypeScriptを使用するのですか?

開発者がApolloを使用してGraphQLサーバーを構築するときはいつでも、ライブラリは次のような「フロントエンド」を生成します。

このインターフェースにより、ユーザーはコードを介してサーバーにクエリまたはミューテーション要求を行うことができます。ただし、部屋の中の象について説明しましょう。あまりユーザーフレンドリーではないようです。フロントエンドにはボタンや便利なインターフェース要素がないため、多くのユーザーがアプリ内を移動するのは難しいかもしれません。その結果、これによりユーザーベースが縮小します。では、この問題をどのように解決するのでしょうか。

これがgraphql-request出番です。これは、ユーザーがGraphQLサーバーでクエリを実行できるようにするオープンソースライブラリです。次の機能を備えています。

  • 軽量—このライブラリは21キロバイトをわずかに超える最小化されているため、アプリのパフォーマンスを維持できます
  • PromiseベースのAPI—これにより非同期アプリケーションのサポートがもたらされます
  • TypeScriptのサポート—TypeScriptgraphql-requestを可能にする多くのライブラリの1つです。Typescriptの主な利点の1つは、安定した予測可能なコードが可能になることです。

たとえば、次のプログラムを見てください。

let myNumber = 9; //here, myNumber is an integer
myNumber = 'hello'; //now it is a string.
myNumber = myNumber + 10; //even though we are adding a string to an integer,
//JavaScript won't return an error. In the real world, it might bring unexpected outputs.
//However, in Typescript, we can tell the compiler..
//what data types we need to choose.
let myNumber:number = 39; //tell TS that we want to declare an integer.
myNumber = 9+'hello'; //returns an error. Therefore, it's easier to debug the program
//this promises stability and security. 

この記事では、GraphQLとTypeScriptを使用してフルスタックアプリを構築します。ここでは、apollo-server-expressパッケージを使用してバックエンドサーバーを構築します。さらに、フロントエンドでは、Nextを使用graphql-requestしてGraphQLAPIを使用します。

サーバーの構築

プロジェクトの初期化

空のNode.jsプロジェクトを初期化するには、次のターミナルコマンドを実行します。

mkdir graphql-ts-tutorial #create project folder 
cd graphql-ts-tutorial 
npm init -y #initialize the app

それが終わったら、コードベースでTypeScriptを使用する必要があることをNodeに通知する必要があります。

#configure our Typescript:
npx tsc --init --rootDir app --outDir dist --esModuleInterop --resolveJsonModule --lib es6 --module commonjs --allowJs true --noImplicitAny true
mkdir app #our main code folder
mkdir dist #Typescript will use this folder to compile our program.

次に、次の依存関係をインストールします。

#development dependencies. Will tell Node that we will use Typescript
npm install -d ts-node @types/node typescript @types/express nodemon
#Installing Apollo Server and its associated modules. Will help us build our GraphQL
#server
npm install apollo-server-express apollo-server-core express graphql

この手順の後、appフォルダに移動します。ここで、次のファイルを作成します。

  • index.ts:メインファイル。これにより、ExpressGraphQLサーバーが実行されます。
  • dataset.ts:これは、クライアントに提供されるデータベースとして機能します
  • Resolvers.ts:このモジュールはユーザーコマンドを処理します。リゾルバーについては、この記事の後半で学習します
  • Schema.ts:名前が示すように、このファイルには、クライアントにデータを送信するために必要な回路図が保存されます

最終的に、フォルダ構造は次のようになります。

データベースの作成

このセクションでは、要求されたデータを送信するために使用されるダミーデータベースを作成します。これを行うには、に移動しapp/dataset.tsて次のコードを記述します。

let people: { id: number; name: string }[] = [
  { id: 1, name: "Cassie" },
  { id: 2, name: "Rue" },
  { id: 3, name: "Lexi" },
];
export default people;
  • まず、というオブジェクトの配列を作成しましたpeople
  • この配列には、タイプとidタイプの2つのフィールドがあります。numbernamestring

スキーマの定義

ここでは、GraphQLサーバーのスキーマを作成します。

簡単に言うと、GraphQLスキーマは、クライアントがAPIから要求できるデータセットの記述です。この概念は、マングースライブラリの概念に似ています。
スキーマを作成するには、app/Schema.tsファイルに移動します。そこで、次のコードを記述します。

import { gql } from "apollo-server-express"; //will create a schema
const Schema = gql`
  type Person {
    id: ID!
    name: String
  }
  #handle user commands
  type Query {
    getAllPeople: [Person] #will return multiple Person instances
    getPerson(id: Int): Person #has an argument of 'id` of type Integer.
  }
`;
export default Schema; 
//export this Schema so we can use it in our project

このコードを1つずつ分解してみましょう。

  • 変数にはSchemaGraphQLスキーマが含まれています
  • まず、Personスキーマを作成しました。idタイプIDnameタイプの2つのフィールドがありますString
  • 後で、クライアントがコマンドを実行すると、サーバーがオブジェクトgetAllPeopleの配列を返すようにGraphQLに指示しました。Person
  • さらに、ユーザーがgetPersonコマンドを使用すると、GraphQLは単一のPersonインスタンスを返します

リゾルバーの作成

スキーマをコーディングしたので、次のステップはリゾルバーを定義することです。
簡単に言うと、リゾルバーはGraphQLクエリの応答を生成する関数のグループです。つまり、リゾルバーはGraphQLクエリハンドラーとして機能します。
Resolvers.ts、次のコードを記述します。

import people from "./dataset"; //get all of the available data from our database.
const Resolvers = {
  Query: {
    getAllPeople: () => people, //if the user runs the getAllPeople command
    //if the user runs the getPerson command:
    getPerson: (_: any, args: any) => { 
      console.log(args);
      //get the object that contains the specified ID.
      return people.find((person) => person.id === args.id);
    },
  },
};
export default Resolvers;
  • ここではQuery、サーバーに送信されるすべての着信クエリを処理するオブジェクトを作成しました
  • ユーザーがgetAllPeopleコマンドを実行すると、プログラムはデータベースに存在するすべてのオブジェクトを返します
  • さらに、getPersonコマンドには引数が必要idです。Personこれにより、IDが一致するインスタンスが返されます
  • 最終的に、アプリとリンクできるようにリゾルバーをエクスポートしました

サーバーの構成

ほぼ完了です。スキーマとリゾルバーの両方を構築したので、次のステップはそれらをリンクすることです。

index.js、次のコードブロックを記述します。

import { ApolloServer } from "apollo-server-express";
import Schema from "./Schema";
import Resolvers from "./Resolvers";
import express from "express";
import { ApolloServerPluginDrainHttpServer } from "apollo-server-core";
import http from "http";

async function startApolloServer(schema: any, resolvers: any) {
  const app = express();
  const httpServer = http.createServer(app);
  const server = new ApolloServer({
    typeDefs: schema,
    resolvers,
    //tell Express to attach GraphQL functionality to the server
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
  }) as any;
  await server.start(); //start the GraphQL server.
  server.applyMiddleware({ app });
  await new Promise<void>((resolve) =>
    httpServer.listen({ port: 4000 }, resolve) //run the server on port 4000
  );
  console.log(`Server ready at http://localhost:4000${server.graphqlPath}`);
}
//in the end, run the server and pass in our Schema and Resolver.
startApolloServer(Schema, Resolvers);

テストしてみましょう!コードを実行するには、次のBashコマンドを使用します。

npx nodemon app/index.ts 

これにより、URLにサーバーが作成されlocalhost:4000/graphqlます。

ここで、UI内で使用可能なスキーマを確認できます。

これは、コードが機能することを意味します。

すべてのGraphQLクエリは操作パネル内にあります。実際の動作を確認するには、次のボックスに次のスニペットを入力してください。

#make a query:
query {
  #get all of the people available in the server
  getAllPeople {
    #procure their IDs and names.
    id
    name
  }
}

結果を確認するには、[実行]ボタンをクリックします。

getPersonクエリを介して特定のエンティティを検索することもできます。

query ($getPersonId: Int) { #the argument will be of type Integer
  getPerson(id: 1) {
    #get the person with the ID of 1
    name
    id
  }
}

突然変異の作成

GraphQLの世界では、ミューテーションはデータベースに副作用をもたらすコマンドです。この一般的な例は次のとおりです。

  • データベースへのユーザーの追加—クライアントがWebサイトにサインアップすると、ユーザーはミューテーションを実行してデータをデータベースに保存します
  • オブジェクトの編集または削除—ユーザーがデータベースからデータを変更または削除した場合、基本的にサーバー上にミューテーションが作成されます。

ミューテーションを処理するには、Schema.tsモジュールに移動します。ここで、Schema変数内に次のコード行を追加します。

const Schema = gql`
  #other code..
  type Mutation {
    #the addPerson commmand will accept an argument of type String.
    #it will return a 'Person' instance. 
    addPerson(name: String): Person
  }
`;

次のステップは、このミューテーションを処理するためのリゾルバーを作成することです。これを行うには、Resolvers.tsファイル内に次のコードブロックを追加します。

const Resolvers = {
  Query: {
    //..further code..
  },
  //code to add:
  //all our mutations go here.
  Mutation: {
    //create our mutation:
    addPerson: (_: any, args: any) => {
      const newPerson = {
        id: people.length + 1, //id field
        name: args.name, //name field
      };
      people.push(newPerson);
      return newPerson; //return the new object's result
    },
  },
};
  • 突然変異は引数addPersonを受け入れますname
  • aが渡されると、プログラムは一致するキーnameを持つ新しいオブジェクトを作成しますname
  • 次に、メソッドを使用してこのオブジェクトをデータセットpushに追加しますpeople
  • 最後に、新しいオブジェクトのプロパティをクライアントに返します

それでおしまい!テストするには、[操作]ウィンドウ内で次のコードを実行します。

#perform a mutation on the server
mutation($name: String) {
  addPerson(name:"Hussain") { #add a new person with the name "Hussain"
    #if the execution succeeds, return its 'id' and 'name` to the user.
    id
    name
  }
}

GraphQLがデータベースに新しいエントリを追加したかどうかを確認しましょう。

query {
  getAllPeople { #get all the results within the 'people' database. 
  #return only their names
  name 
  }
}

クライアントの構築

サーバーの構築に成功しました。このセクションでは、Nextを使用して、サーバーをリッスンし、UIにデータをレンダリングするクライアントアプリを構築します。

最初のステップとして、次のように空のNext.jsアプリを初期化します。

npx create-next-app@latest graphql-client --ts
touch constants.tsx #our query variables go here.

GraphQL操作を実行するには、graphql-requestライブラリーを使用します。これは最小限のオープンソースモジュールであり、サーバーでミューテーションとクエリを実行するのに役立ちます。

npm install graphql-request graphql
npm install react-hook-form #to capture user input

クエリ変数の作成

このセクションでは、GraphQL操作を行うのに役立つクエリとミューテーションをコーディングします。これを行うには、に移動しconstants.tsxて次のコードを追加します。

import { gql } from "graphql-request";
//create our query
const getAllPeopleQuery = gql`
  query {
    getAllPeople { #run the getAllPeople command
      id
      name
    }
  }
`;
//Next, declare a mutation
const addPersonMutation = gql`
  mutation addPeople($name: String!) {
    addPerson(name: $name) { #add a new entry. Argument will be 'name'
      id
      name
    }
  }
`;
export { getAllPeopleQuery, addPersonMutation };
  • 最初の部分では、getAllPeopleQuery変数を作成しました。ユーザーがこのクエリを実行すると、プログラムはサーバーにデータベースに存在するすべてのエントリを取得するように指示します
  • 後で、addPersonミューテーションはGraphQLに、尊重されたnameフィールドを持つ新しいエントリを追加するように指示します
  • 最後に、exportキーワードを使用して変数をプロジェクトの残りの部分にリンクしました

クエリの実行

pages/index.ts、次のコードを記述します。

import type { NextPage, GetStaticProps, InferGetStaticPropsType } from "next";
import { request } from "graphql-request"; //allows us to perform a request on our server
import { getAllPeopleQuery } from "../constants"; 
import Link from "next/link";
const Home: NextPage = ({
  result, //extract the 'result' prop 
}: InferGetStaticPropsType<typeof getStaticProps>) => {
  return (
    <div className={styles.container}>
      {result.map((item: any) => { //render the 'result' array to the UI 
        return <p key={item.id}>{item.name}</p>;
      })}
    <Link href="/addpage">Add a new entry </Link>
    </div>
  );
};
//fetch data from the server
export const getStaticProps: GetStaticProps = async () => {
  //the first argument is the URL of our GraphQL server
  const res = await request("http://localhost:4000/graphql", getAllPeopleQuery);
  const result = res.getAllPeople;
  return {
    props: {
      result,
    }, // will be passed to the page component as props
  };
};
export default Home;

このコードの内訳は次のとおりです。

  • このメソッドでは、GraphQLサーバーでコマンドgetStaticPropsを実行するようにNextに指示しましたgetAllPeople
  • Homeその後、機能コンポーネントへの応答を返しました。これは、結果をUIにレンダリングできることを意味します
  • 次に、プログラムはこのメソッドを使用して、コマンドのすべての結果をUImapにレンダリングしました。getAllPeople各段落要素にはname、各エントリのフィールドが表示されます
  • さらに、コンポーネントを使用してユーザーをルートLinkにリダイレクトしました。addpageこれにより、ユーザーPersonはテーブルに新しいインスタンスを追加できます

コードをテストするには、次のターミナルコマンドを実行します。

npm run dev

これが結果になります:

GraphQLサーバーはリアルタイムで更新されます。

突然変異の実行

graphql-requestクエリの実行に成功したので、ライブラリを介してミューテーションを実行することもできます。

フォルダ内pagesに、という名前の新しいファイルを作成しますaddpage.tsx。名前が示すように、このコンポーネントを使用すると、ユーザーはデータベースに新しいエントリを追加できます。ここでは、次のコードブロックを作成することから始めます。

import type { NextPage, GetStaticProps, InferGetStaticPropsType } from "next";
import { request } from "graphql-request";
import { addPersonMutation } from "../constants";
const AddPage: NextPage = () => {
  return (
    <div>
      <p>We will add a new entry here. </p>
    </div>
  );
};
export default AddPage;

このコードでは、テキストを含む空白のページを作成しています。これは、URLルーティングシステムが機能するかどうかを確認するために行っています。

これは、ルーティングを正常に使用したことを意味します。次に、このスニペットをaddpage.tsxファイルに書き込みます。

import { useForm } from "react-hook-form";
const { register, handleSubmit } = useForm();
//if the user submits the form, then the program will output the value of their input.
const onSubmit = (data: any) => console.log(data);
return (
  <div>
    <form onSubmit={handleSubmit(onSubmit)}> {/*Bind our handler to this form.*/}
      {/* The user's input will be saved within the 'name' property */}
      <input defaultValue="test" {...register("name")} />
      <input type="submit" />
    </form>
  </div>
);

これが出力になります:

 

ユーザーの入力を正常にキャプチャしたので、最後のステップはサーバーにユーザーのエントリを追加することです。

これを行うには、ファイルにあるonSubmitハンドラーを次のように変更します。pages/addpage.tsx

const onSubmit = async (data: any) => {
  const response = await request(
    "http://localhost:4000/graphql",
    addPersonMutation,
    data
  );
  console.log(response);
};
  • requestここでは、関数を介してGraphQLサーバーへのミューテーションリクエストを実行しています
  • さらに、addPersonmutationコマンドをリクエストヘッダーにも渡しました。これにより、GraphQLにaddMutationサーバーでアクションを実行するように指示されます

これが結果になります:

これで完了です。

結論

これがこのプロジェクトの完全なソースコードです。

この記事では、GraphQLとTypeScriptを使用してフルスタックアプリを作成する方法を学びました。これらは両方とも、今日需要が高いため、プログラミングの世界では非常に重要なスキルです。

このコードで問題が発生した場合は、この概念を完全に理解できるように、コードを分解して試してみることをお勧めします。

読んでくれてありがとう!ハッピーコーディング!

このストーリーは、もともとhttps://blog.logrocket.com/build-graphql-app-node-js-typescript-graphql-request/で公開されました

#graphql #typescript #nodejs 

TypeScriptとgraphql-requestを使用してNode.jsでGraphQLアプリを構築します

Face Recognition with OpenCV and Python

Introduction

What is face recognition? Or what is recognition? When you look at an apple fruit, your mind immediately tells you that this is an apple fruit. This process, your mind telling you that this is an apple fruit is recognition in simple words. So what is face recognition then? I am sure you have guessed it right. When you look at your friend walking down the street or a picture of him, you recognize that he is your friend Paulo. Interestingly when you look at your friend or a picture of him you look at his face first before looking at anything else. Ever wondered why you do that? This is so that you can recognize him by looking at his face. Well, this is you doing face recognition.

But the real question is how does face recognition works? It is quite simple and intuitive. Take a real life example, when you meet someone first time in your life you don't recognize him, right? While he talks or shakes hands with you, you look at his face, eyes, nose, mouth, color and overall look. This is your mind learning or training for the face recognition of that person by gathering face data. Then he tells you that his name is Paulo. At this point your mind knows that the face data it just learned belongs to Paulo. Now your mind is trained and ready to do face recognition on Paulo's face. Next time when you will see Paulo or his face in a picture you will immediately recognize him. This is how face recognition work. The more you will meet Paulo, the more data your mind will collect about Paulo and especially his face and the better you will become at recognizing him.

Now the next question is how to code face recognition with OpenCV, after all this is the only reason why you are reading this article, right? OK then. You might say that our mind can do these things easily but to actually code them into a computer is difficult? Don't worry, it is not. Thanks to OpenCV, coding face recognition is as easier as it feels. The coding steps for face recognition are same as we discussed it in real life example above.

  • Training Data Gathering: Gather face data (face images in this case) of the persons you want to recognize
  • Training of Recognizer: Feed that face data (and respective names of each face) to the face recognizer so that it can learn.
  • Recognition: Feed new faces of the persons and see if the face recognizer you just trained recognizes them.

OpenCV comes equipped with built in face recognizer, all you have to do is feed it the face data. It's that simple and this how it will look once we are done coding it.

visualization

OpenCV Face Recognizers

OpenCV has three built in face recognizers and thanks to OpenCV's clean coding, you can use any of them by just changing a single line of code. Below are the names of those face recognizers and their OpenCV calls.

  1. EigenFaces Face Recognizer Recognizer - cv2.face.createEigenFaceRecognizer()
  2. FisherFaces Face Recognizer Recognizer - cv2.face.createFisherFaceRecognizer()
  3. Local Binary Patterns Histograms (LBPH) Face Recognizer - cv2.face.createLBPHFaceRecognizer()

We have got three face recognizers but do you know which one to use and when? Or which one is better? I guess not. So why not go through a brief summary of each, what you say? I am assuming you said yes :) So let's dive into the theory of each.

EigenFaces Face Recognizer

This algorithm considers the fact that not all parts of a face are equally important and equally useful. When you look at some one you recognize him/her by his distinct features like eyes, nose, cheeks, forehead and how they vary with respect to each other. So you are actually focusing on the areas of maximum change (mathematically speaking, this change is variance) of the face. For example, from eyes to nose there is a significant change and same is the case from nose to mouth. When you look at multiple faces you compare them by looking at these parts of the faces because these parts are the most useful and important components of a face. Important because they catch the maximum change among faces, change the helps you differentiate one face from the other. This is exactly how EigenFaces face recognizer works.

EigenFaces face recognizer looks at all the training images of all the persons as a whole and try to extract the components which are important and useful (the components that catch the maximum variance/change) and discards the rest of the components. This way it not only extracts the important components from the training data but also saves memory by discarding the less important components. These important components it extracts are called principal components. Below is an image showing the principal components extracted from a list of faces.

Principal Components eigenfaces_opencv source

You can see that principal components actually represent faces and these faces are called eigen faces and hence the name of the algorithm.

So this is how EigenFaces face recognizer trains itself (by extracting principal components). Remember, it also keeps a record of which principal component belongs to which person. One thing to note in above image is that Eigenfaces algorithm also considers illumination as an important component.

Later during recognition, when you feed a new image to the algorithm, it repeats the same process on that image as well. It extracts the principal component from that new image and compares that component with the list of components it stored during training and finds the component with the best match and returns the person label associated with that best match component.

Easy peasy, right? Next one is easier than this one.

FisherFaces Face Recognizer

This algorithm is an improved version of EigenFaces face recognizer. Eigenfaces face recognizer looks at all the training faces of all the persons at once and finds principal components from all of them combined. By capturing principal components from all the of them combined you are not focusing on the features that discriminate one person from the other but the features that represent all the persons in the training data as a whole.

This approach has drawbacks, for example, images with sharp changes (like light changes which is not a useful feature at all) may dominate the rest of the images and you may end up with features that are from external source like light and are not useful for discrimination at all. In the end, your principal components will represent light changes and not the actual face features.

Fisherfaces algorithm, instead of extracting useful features that represent all the faces of all the persons, it extracts useful features that discriminate one person from the others. This way features of one person do not dominate over the others and you have the features that discriminate one person from the others.

Below is an image of features extracted using Fisherfaces algorithm.

Fisher Faces eigenfaces_opencv source

You can see that features extracted actually represent faces and these faces are called fisher faces and hence the name of the algorithm.

One thing to note here is that even in Fisherfaces algorithm if multiple persons have images with sharp changes due to external sources like light they will dominate over other features and affect recognition accuracy.

Getting bored with this theory? Don't worry, only one face recognizer is left and then we will dive deep into the coding part.

Local Binary Patterns Histograms (LBPH) Face Recognizer

I wrote a detailed explaination on Local Binary Patterns Histograms in my previous article on face detection using local binary patterns histograms. So here I will just give a brief overview of how it works.

We know that Eigenfaces and Fisherfaces are both affected by light and in real life we can't guarantee perfect light conditions. LBPH face recognizer is an improvement to overcome this drawback.

Idea is to not look at the image as a whole instead find the local features of an image. LBPH alogrithm try to find the local structure of an image and it does that by comparing each pixel with its neighboring pixels.

Take a 3x3 window and move it one image, at each move (each local part of an image), compare the pixel at the center with its neighbor pixels. The neighbors with intensity value less than or equal to center pixel are denoted by 1 and others by 0. Then you read these 0/1 values under 3x3 window in a clockwise order and you will have a binary pattern like 11100011 and this pattern is local to some area of the image. You do this on whole image and you will have a list of local binary patterns.

LBP Labeling LBP labeling

Now you get why this algorithm has Local Binary Patterns in its name? Because you get a list of local binary patterns. Now you may be wondering, what about the histogram part of the LBPH? Well after you get a list of local binary patterns, you convert each binary pattern into a decimal number (as shown in above image) and then you make a histogram of all of those values. A sample histogram looks like this.

Sample Histogram LBP labeling

I guess this answers the question about histogram part. So in the end you will have one histogram for each face image in the training data set. That means if there were 100 images in training data set then LBPH will extract 100 histograms after training and store them for later recognition. Remember, algorithm also keeps track of which histogram belongs to which person.

Later during recognition, when you will feed a new image to the recognizer for recognition it will generate a histogram for that new image, compare that histogram with the histograms it already has, find the best match histogram and return the person label associated with that best match histogram. 

Below is a list of faces and their respective local binary patterns images. You can see that the LBP images are not affected by changes in light conditions.

LBP Faces LBP faces source

The theory part is over and now comes the coding part! Ready to dive into coding? Let's get into it then.

Coding Face Recognition with OpenCV

The Face Recognition process in this tutorial is divided into three steps.

  1. Prepare training data: In this step we will read training images for each person/subject along with their labels, detect faces from each image and assign each detected face an integer label of the person it belongs to.
  2. Train Face Recognizer: In this step we will train OpenCV's LBPH face recognizer by feeding it the data we prepared in step 1.
  3. Testing: In this step we will pass some test images to face recognizer and see if it predicts them correctly.

[There should be a visualization diagram for above steps here]

To detect faces, I will use the code from my previous article on face detection. So if you have not read it, I encourage you to do so to understand how face detection works and its Python coding.

Import Required Modules

Before starting the actual coding we need to import the required modules for coding. So let's import them first.

  • cv2: is OpenCV module for Python which we will use for face detection and face recognition.
  • os: We will use this Python module to read our training directories and file names.
  • numpy: We will use this module to convert Python lists to numpy arrays as OpenCV face recognizers accept numpy arrays.
#import OpenCV module
import cv2
#import os module for reading training data directories and paths
import os
#import numpy to convert python lists to numpy arrays as 
#it is needed by OpenCV face recognizers
import numpy as np

#matplotlib for display our images
import matplotlib.pyplot as plt
%matplotlib inline 

Training Data

The more images used in training the better. Normally a lot of images are used for training a face recognizer so that it can learn different looks of the same person, for example with glasses, without glasses, laughing, sad, happy, crying, with beard, without beard etc. To keep our tutorial simple we are going to use only 12 images for each person.

So our training data consists of total 2 persons with 12 images of each person. All training data is inside training-data folder. training-data folder contains one folder for each person and each folder is named with format sLabel (e.g. s1, s2) where label is actually the integer label assigned to that person. For example folder named s1 means that this folder contains images for person 1. The directory structure tree for training data is as follows:

training-data
|-------------- s1
|               |-- 1.jpg
|               |-- ...
|               |-- 12.jpg
|-------------- s2
|               |-- 1.jpg
|               |-- ...
|               |-- 12.jpg

The test-data folder contains images that we will use to test our face recognizer after it has been successfully trained.

As OpenCV face recognizer accepts labels as integers so we need to define a mapping between integer labels and persons actual names so below I am defining a mapping of persons integer labels and their respective names.

Note: As we have not assigned label 0 to any person so the mapping for label 0 is empty.

#there is no label 0 in our training data so subject name for index/label 0 is empty
subjects = ["", "Tom Cruise", "Shahrukh Khan"]

Prepare training data

You may be wondering why data preparation, right? Well, OpenCV face recognizer accepts data in a specific format. It accepts two vectors, one vector is of faces of all the persons and the second vector is of integer labels for each face so that when processing a face the face recognizer knows which person that particular face belongs too.

For example, if we had 2 persons and 2 images for each person.

PERSON-1    PERSON-2   

img1        img1         
img2        img2

Then the prepare data step will produce following face and label vectors.

FACES                        LABELS

person1_img1_face              1
person1_img2_face              1
person2_img1_face              2
person2_img2_face              2

Preparing data step can be further divided into following sub-steps.

  1. Read all the folder names of subjects/persons provided in training data folder. So for example, in this tutorial we have folder names: s1, s2.
  2. For each subject, extract label number. Do you remember that our folders have a special naming convention? Folder names follow the format sLabel where Label is an integer representing the label we have assigned to that subject. So for example, folder name s1 means that the subject has label 1, s2 means subject label is 2 and so on. The label extracted in this step is assigned to each face detected in the next step.
  3. Read all the images of the subject, detect face from each image.
  4. Add each face to faces vector with corresponding subject label (extracted in above step) added to labels vector.

[There should be a visualization for above steps here]

Did you read my last article on face detection? No? Then you better do so right now because to detect faces, I am going to use the code from my previous article on face detection. So if you have not read it, I encourage you to do so to understand how face detection works and its coding. Below is the same code.

#function to detect face using OpenCV
def detect_face(img):
    #convert the test image to gray image as opencv face detector expects gray images
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    #load OpenCV face detector, I am using LBP which is fast
    #there is also a more accurate but slow Haar classifier
    face_cascade = cv2.CascadeClassifier('opencv-files/lbpcascade_frontalface.xml')

    #let's detect multiscale (some images may be closer to camera than others) images
    #result is a list of faces
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5);
    
    #if no faces are detected then return original img
    if (len(faces) == 0):
        return None, None
    
    #under the assumption that there will be only one face,
    #extract the face area
    (x, y, w, h) = faces[0]
    
    #return only the face part of the image
    return gray[y:y+w, x:x+h], faces[0]

I am using OpenCV's LBP face detector. On line 4, I convert the image to grayscale because most operations in OpenCV are performed in gray scale, then on line 8 I load LBP face detector using cv2.CascadeClassifier class. After that on line 12 I use cv2.CascadeClassifier class' detectMultiScale method to detect all the faces in the image. on line 20, from detected faces I only pick the first face because in one image there will be only one face (under the assumption that there will be only one prominent face). As faces returned by detectMultiScale method are actually rectangles (x, y, width, height) and not actual faces images so we have to extract face image area from the main image. So on line 23 I extract face area from gray image and return both the face image area and face rectangle.

Now you have got a face detector and you know the 4 steps to prepare the data, so are you ready to code the prepare data step? Yes? So let's do it.

#this function will read all persons' training images, detect face from each image
#and will return two lists of exactly same size, one list 
# of faces and another list of labels for each face
def prepare_training_data(data_folder_path):
    
    #------STEP-1--------
    #get the directories (one directory for each subject) in data folder
    dirs = os.listdir(data_folder_path)
    
    #list to hold all subject faces
    faces = []
    #list to hold labels for all subjects
    labels = []
    
    #let's go through each directory and read images within it
    for dir_name in dirs:
        
        #our subject directories start with letter 's' so
        #ignore any non-relevant directories if any
        if not dir_name.startswith("s"):
            continue;
            
        #------STEP-2--------
        #extract label number of subject from dir_name
        #format of dir name = slabel
        #, so removing letter 's' from dir_name will give us label
        label = int(dir_name.replace("s", ""))
        
        #build path of directory containin images for current subject subject
        #sample subject_dir_path = "training-data/s1"
        subject_dir_path = data_folder_path + "/" + dir_name
        
        #get the images names that are inside the given subject directory
        subject_images_names = os.listdir(subject_dir_path)
        
        #------STEP-3--------
        #go through each image name, read image, 
        #detect face and add face to list of faces
        for image_name in subject_images_names:
            
            #ignore system files like .DS_Store
            if image_name.startswith("."):
                continue;
            
            #build image path
            #sample image path = training-data/s1/1.pgm
            image_path = subject_dir_path + "/" + image_name

            #read image
            image = cv2.imread(image_path)
            
            #display an image window to show the image 
            cv2.imshow("Training on image...", image)
            cv2.waitKey(100)
            
            #detect face
            face, rect = detect_face(image)
            
            #------STEP-4--------
            #for the purpose of this tutorial
            #we will ignore faces that are not detected
            if face is not None:
                #add face to list of faces
                faces.append(face)
                #add label for this face
                labels.append(label)
            
    cv2.destroyAllWindows()
    cv2.waitKey(1)
    cv2.destroyAllWindows()
    
    return faces, labels

I have defined a function that takes the path, where training subjects' folders are stored, as parameter. This function follows the same 4 prepare data substeps mentioned above.

(step-1) On line 8 I am using os.listdir method to read names of all folders stored on path passed to function as parameter. On line 10-13 I am defining labels and faces vectors.

(step-2) After that I traverse through all subjects' folder names and from each subject's folder name on line 27 I am extracting the label information. As folder names follow the sLabel naming convention so removing the letter s from folder name will give us the label assigned to that subject.

(step-3) On line 34, I read all the images names of of the current subject being traversed and on line 39-66 I traverse those images one by one. On line 53-54 I am using OpenCV's imshow(window_title, image) along with OpenCV's waitKey(interval) method to display the current image being traveresed. The waitKey(interval) method pauses the code flow for the given interval (milliseconds), I am using it with 100ms interval so that we can view the image window for 100ms. On line 57, I detect face from the current image being traversed.

(step-4) On line 62-66, I add the detected face and label to their respective vectors.

But a function can't do anything unless we call it on some data that it has to prepare, right? Don't worry, I have got data of two beautiful and famous celebrities. I am sure you will recognize them!

training-data

Let's call this function on images of these beautiful celebrities to prepare data for training of our Face Recognizer. Below is a simple code to do that.

#let's first prepare our training data
#data will be in two lists of same size
#one list will contain all the faces
#and other list will contain respective labels for each face
print("Preparing data...")
faces, labels = prepare_training_data("training-data")
print("Data prepared")

#print total faces and labels
print("Total faces: ", len(faces))
print("Total labels: ", len(labels))
Preparing data...
Data prepared
Total faces:  23
Total labels:  23

This was probably the boring part, right? Don't worry, the fun stuff is coming up next. It's time to train our own face recognizer so that once trained it can recognize new faces of the persons it was trained on. Read? Ok then let's train our face recognizer.

Train Face Recognizer

As we know, OpenCV comes equipped with three face recognizers.

  1. EigenFace Recognizer: This can be created with cv2.face.createEigenFaceRecognizer()
  2. FisherFace Recognizer: This can be created with cv2.face.createFisherFaceRecognizer()
  3. Local Binary Patterns Histogram (LBPH): This can be created with cv2.face.LBPHFisherFaceRecognizer()

I am going to use LBPH face recognizer but you can use any face recognizer of your choice. No matter which of the OpenCV's face recognizer you use the code will remain the same. You just have to change one line, the face recognizer initialization line given below.

#create our LBPH face recognizer 
face_recognizer = cv2.face.createLBPHFaceRecognizer()

#or use EigenFaceRecognizer by replacing above line with 
#face_recognizer = cv2.face.createEigenFaceRecognizer()

#or use FisherFaceRecognizer by replacing above line with 
#face_recognizer = cv2.face.createFisherFaceRecognizer()

Now that we have initialized our face recognizer and we also have prepared our training data, it's time to train the face recognizer. We will do that by calling the train(faces-vector, labels-vector) method of face recognizer.

#train our face recognizer of our training faces
face_recognizer.train(faces, np.array(labels))

Did you notice that instead of passing labels vector directly to face recognizer I am first converting it to numpy array? This is because OpenCV expects labels vector to be a numpy array.

Still not satisfied? Want to see some action? Next step is the real action, I promise!

Prediction

Now comes my favorite part, the prediction part. This is where we actually get to see if our algorithm is actually recognizing our trained subjects's faces or not. We will take two test images of our celeberities, detect faces from each of them and then pass those faces to our trained face recognizer to see if it recognizes them.

Below are some utility functions that we will use for drawing bounding box (rectangle) around face and putting celeberity name near the face bounding box.

#function to draw rectangle on image 
#according to given (x, y) coordinates and 
#given width and heigh
def draw_rectangle(img, rect):
    (x, y, w, h) = rect
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
    
#function to draw text on give image starting from
#passed (x, y) coordinates. 
def draw_text(img, text, x, y):
    cv2.putText(img, text, (x, y), cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 0), 2)

First function draw_rectangle draws a rectangle on image based on passed rectangle coordinates. It uses OpenCV's built in function cv2.rectangle(img, topLeftPoint, bottomRightPoint, rgbColor, lineWidth) to draw rectangle. We will use it to draw a rectangle around the face detected in test image.

Second function draw_text uses OpenCV's built in function cv2.putText(img, text, startPoint, font, fontSize, rgbColor, lineWidth) to draw text on image.

Now that we have the drawing functions, we just need to call the face recognizer's predict(face) method to test our face recognizer on test images. Following function does the prediction for us.

#this function recognizes the person in image passed
#and draws a rectangle around detected face with name of the 
#subject
def predict(test_img):
    #make a copy of the image as we don't want to chang original image
    img = test_img.copy()
    #detect face from the image
    face, rect = detect_face(img)

    #predict the image using our face recognizer 
    label= face_recognizer.predict(face)
    #get name of respective label returned by face recognizer
    label_text = subjects[label]
    
    #draw a rectangle around face detected
    draw_rectangle(img, rect)
    #draw name of predicted person
    draw_text(img, label_text, rect[0], rect[1]-5)
    
    return img
  • line-6 read the test image
  • line-7 detect face from test image
  • line-11 recognize the face by calling face recognizer's predict(face) method. This method will return a lable
  • line-12 get the name associated with the label
  • line-16 draw rectangle around the detected face
  • line-18 draw name of predicted subject above face rectangle

Now that we have the prediction function well defined, next step is to actually call this function on our test images and display those test images to see if our face recognizer correctly recognized them. So let's do it. This is what we have been waiting for.

print("Predicting images...")

#load test images
test_img1 = cv2.imread("test-data/test1.jpg")
test_img2 = cv2.imread("test-data/test2.jpg")

#perform a prediction
predicted_img1 = predict(test_img1)
predicted_img2 = predict(test_img2)
print("Prediction complete")

#create a figure of 2 plots (one for each test image)
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))

#display test image1 result
ax1.imshow(cv2.cvtColor(predicted_img1, cv2.COLOR_BGR2RGB))

#display test image2 result
ax2.imshow(cv2.cvtColor(predicted_img2, cv2.COLOR_BGR2RGB))

#display both images
cv2.imshow("Tom cruise test", predicted_img1)
cv2.imshow("Shahrukh Khan test", predicted_img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(1)
cv2.destroyAllWindows()
Predicting images...
Prediction complete

wohooo! Is'nt it beautiful? Indeed, it is!

End Notes

Face Recognition is a fascinating idea to work on and OpenCV has made it extremely simple and easy for us to code it. It just takes a few lines of code to have a fully working face recognition application and we can switch between all three face recognizers with a single line of code change. It's that simple.

Although EigenFaces, FisherFaces and LBPH face recognizers are good but there are even better ways to perform face recognition like using Histogram of Oriented Gradients (HOGs) and Neural Networks. So the more advanced face recognition algorithms are now a days implemented using a combination of OpenCV and Machine learning. I have plans to write some articles on those more advanced methods as well, so stay tuned!

Download Details:
Author: informramiz
Source Code: https://github.com/informramiz/opencv-face-recognition-python
License: MIT License

#opencv  #python #facerecognition 

Face Recognition with OpenCV and Python
Monty  Boehm

Monty Boehm

1645849980

Eradicate: Removes Commented-out Code From Python Files

eradicate

eradicate removes commented-out code from Python files.

Introduction

With modern revision control available, there is no reason to save commented-out code to your repository. eradicate helps cleans up existing junk comments. It does this by detecting block comments that contain valid Python syntax that are likely to be commented out code. (It avoids false positives like the sentence this is not good, which is valid Python syntax, but is probably not code.)

Example

$ eradicate --in-place example.py

Before running eradicate.

#import os
# from foo import junk
#a = 3
a = 4
#foo(1, 2, 3)

def foo(x, y, z):
    # print('hello')
    print(x, y, z)

    # This is a real comment.
    #return True
    return False

After running eradicate.

a = 4

def foo(x, y, z):
    print(x, y, z)

    # This is a real comment.
    return False

Whitelisting

False positives can happen so there is a whitelist feature to fix them shorthand. You can either add entries to the default whitelist with --whitelist-extend or overwrite the default with --whitelist. Both arguments expect a string of # separated regex strings (whitespaces are preserved). E.g. eradicate --whitelist "foo#b a r" filename Those regex strings are matched case insensitive against the start of the comment itself.

For the default whitelist please see eradicate.py.

Related

There are different tools, plugins, and integrations for eradicate users:

Author: Myint
Source Code: https://github.com/myint/eradicate 
License: 

#python 

Eradicate: Removes Commented-out Code From Python Files
Kim Hans  Jin

Kim Hans Jin

1597823927

Creating a CRUD Desktop Application using Python 3 and MySQL Database Server

This is a simple CRUD(Create, Retrieve, Update, and Delete) database record desktop application. Its name is Student Management System. A record with Student’s FirstName, LastName, ContactNo, City, State, and date of birth are inserted into a table named student_master in the student database. MySQL Database Server. Python 3.6, Tkinter, GUI builder, and MySQL-connector are used. For choosing the date of birth, the tkcalendar widget is used. Installation of the tkcalender widget from the command prompt is displayed below.

The name of the Python code file is StudentManager.py. After executing the file, the application window is displayed below. In that window, a new student record can be added. All the added records in MySQL database table are displayed in a Tkinter Treeview widget, shown below.

Inserting Student Record

All the student data is filled up and the date of birth is chosen from a drop-down calendar widget, as shown below, and the register button is clicked to add the record to the database.

 

The Student Record is inserted successfully and displayed below.

If the inserted record state name is spelled wrong or we want to change the date of birth, the record must be updated. Please select the record in the displayed widget by clicking it. All data is displayed in the above boxes to update and delete the record.

Please edit the fields displayed in boxes and update them by clicking the Update button.

 

A student record can be searched by entering roll no and clicking the search button as shown above.

 

To display all records, please click the Show All button.

 

To Delete a record, select the record in the display widget and click the delete button.

 

 

StudentManager.py Code

from tkcalendar import Calendar, DateEntry  
import tkinter as tk  
import tkinter.messagebox as mb  
import tkinter.ttk as ttk  
## Connecting to the database  
## importing 'mysql.connector' for connection to mysql database  
import mysql.connector  
## connecting to the database using 'connect()' method  
## it takes 3 required parameters 'host', 'user', 'password'  
#Please change user and password values to your  
#user and password values to connect to MySQL Database server   
db_connection = mysql.connector.connect(  
host="localhost",  
user="raichand70",  
password="1America")  
# creating database_cursor to perform SQL operation  
db_cursor = db_connection.cursor(buffered=True) # "buffered=True".makes db_cursor.row_count return actual number of records selected otherwise would return -1  
class StudentApp(tk.Tk):  
def __init__(self):  
super().__init__()  
self.title("Student Management System")  
self.geometry("800x650+351+174")  
self.lblTitle = tk.Label(self, text="Student Management System", font=("Helvetica", 16), bg="yellow", fg="green")  
self.lblFName = tk.Label(self, text="Enter FirstName:", font=("Helvetica", 10), bg="blue", fg="yellow")  
self.lblLName = tk.Label(self, text="Enter LastName:", font=("Helvetica", 10), bg="blue", fg="yellow")  
self.lblContactNo = tk.Label(self, text="Enter Contact No:", font=("Helvetica", 10), bg="blue", fg="yellow")  
self.lblCity = tk.Label(self, text="Enter City:", font=("Helvetica", 10), bg="blue", fg="yellow")  
self.lblState = tk.Label(self, text="Enter State:", font=("Helvetica", 10), bg="blue", fg="yellow")  
self.lblDOB = tk.Label(self, text="Choose Date of Birth:", font=("Helvetica", 10), bg="blue", fg="yellow")  
self.lblSelect = tk.Label(self, text="Please select one record below to update or delete", font=("Helvetica", 10), bg="blue", fg="yellow")  
self.lblSearch = tk.Label(self, text="Please Enter Roll No:",font=("Helvetica", 10), bg="blue", fg="yellow")  
self.entFName = tk.Entry(self)  
self.entLName = tk.Entry(self)  
self.entContact = tk.Entry(self)  
self.entCity = tk.Entry(self)  
self.entState = tk.Entry(self)  
self.calDOB = DateEntry(self, width=12, background='darkblue',  
foreground='white', borderwidth=2, year=1950,locale='en_US', date_pattern='y-mm-dd')  
#self.entDOB = tk.Entry(self)  
self.entSearch = tk.Entry(self)  
self.btn_register = tk.Button(self, text="Register", font=("Helvetica", 11), bg="yellow", fg="blue",  
command=self.register_student)  
self.btn_update = tk.Button(self,text="Update",font=("Helvetica",11),bg="yellow", fg="blue",command=self.update_student_data)  
self.btn_delete = tk.Button(self, text="Delete", font=("Helvetica", 11), bg="yellow", fg="blue",  
command=self.delete_student_data)  
self.btn_clear = tk.Button(self, text="Clear", font=("Helvetica", 11), bg="yellow", fg="blue",  
command=self.clear_form)  
self.btn_show_all = tk.Button(self, text="Show All", font=("Helvetica", 11), bg="yellow", fg="blue",  
command=self.load_student_data)  
self.btn_search = tk.Button(self, text="Search", font=("Helvetica", 11), bg="yellow", fg="blue",  
command=self.show_search_record)  
self.btn_exit = tk.Button(self, text="Exit", font=("Helvetica", 16), bg="yellow", fg="blue",command=self.exit)  
columns = ("#1", "#2", "#3", "#4", "#5", "#6", "#7")  
self.tvStudent= ttk.Treeview(self,show="headings",height="5", columns=columns)  
self.tvStudent.heading('#1', text='RollNo', anchor='center')  
self.tvStudent.column('#1', width=60, anchor='center', stretch=False)  
self.tvStudent.heading('#2', text='FirstName', anchor='center')  
self.tvStudent.column('#2', width=10, anchor='center', stretch=True)  
self.tvStudent.heading('#3', text='LastName', anchor='center')  
self.tvStudent.column('#3',width=10, anchor='center', stretch=True)  
self.tvStudent.heading('#4', text='City', anchor='center')  
self.tvStudent.column('#4',width=10, anchor='center', stretch=True)  
self.tvStudent.heading('#5', text='State', anchor='center')  
self.tvStudent.column('#5',width=10, anchor='center', stretch=True)  
self.tvStudent.heading('#6', text='PhoneNumber', anchor='center')  
self.tvStudent.column('#6', width=10, anchor='center', stretch=True)  
self.tvStudent.heading('#7', text='Date of Birth', anchor='center')  
self.tvStudent.column('#7', width=10, anchor='center', stretch=True)  
#Scroll bars are set up below considering placement position(x&y) ,height and width of treeview widget  
vsb= ttk.Scrollbar(self, orient=tk.VERTICAL,command=self.tvStudent.yview)  
vsb.place(x=40 + 640 + 1, y=310, height=180 + 20)  
self.tvStudent.configure(yscroll=vsb.set)  
hsb = ttk.Scrollbar(self, orient=tk.HORIZONTAL, command=self.tvStudent.xview)  
hsb.place(x=40 , y=310+200+1, width=620 + 20)  
self.tvStudent.configure(xscroll=hsb.set)  
self.tvStudent.bind("<<TreeviewSelect>>", self.show_selected_record)  
self.lblTitle.place(x=280, y=30, height=27, width=300)  
self.lblFName.place(x=175, y=70, height=23, width=100)  
self.lblLName.place(x=175, y=100, height=23, width=100)  
self.lblContactNo.place(x=171, y=129, height=23, width=104)  
self.lblCity.place(x=210, y=158, height=23, width=65)  
self.lblState.place(x=205, y=187, height=23, width=71)  
self.lblDOB.place(x=148, y=217, height=23, width=128)  
self.lblSelect.place(x=150, y=280, height=23, width=400)  
self.lblSearch.place(x=174, y=560, height=23, width=134)  
self.entFName.place(x=277, y=72, height=21, width=186)  
self.entLName.place(x=277, y=100, height=21, width=186)  
self.entContact.place(x=277, y=129, height=21, width=186)  
self.entCity.place(x=277, y=158, height=21, width=186)  
self.entState.place(x=278, y=188, height=21, width=186)  
self.calDOB.place(x=278, y=218, height=21, width=186)  
self.entSearch.place(x=310, y=560, height=21, width=186)  
self.btn_register.place(x=290, y=245, height=25, width=76)  
self.btn_update.place(x=370, y=245, height=25, width=76)  
self.btn_delete.place(x=460, y=245, height=25, width=76)  
self.btn_clear.place(x=548, y=245, height=25, width=76)  
self.btn_show_all.place(x=630, y=245, height=25, width=76)  
self.btn_search.place(x=498, y=558, height=26, width=60)  
self.btn_exit.place(x=320, y=610, height=31, width=60)  
self.tvStudent.place(x=40, y=310, height=200, width=640)  
self.create_table()  
self.load_student_data()  
def clear_form(self):  
self.entFName.delete(0, tk.END)  
self.entLName.delete(0, tk.END)  
self.entContact.delete(0, tk.END)  
self.entCity.delete(0, tk.END)  
self.entState.delete(0, tk.END)  
self.calDOB.delete(0, tk.END)  
def exit(self):  
MsgBox = mb.askquestion('Exit Application', 'Are you sure you want to exit the application', icon='warning')  
if MsgBox == 'yes':  
self.destroy()  
def delete_student_data(self):  
MsgBox = mb.askquestion('Delete Record', 'Are you sure! you want to delete selected student record', icon='warning')  
if MsgBox == 'yes':  
if db_connection.is_connected() == False:  
db_connection.connect()  
db_cursor.execute("use Student") # Interact with Student Database  
# deleteing selected student record  
Delete = "delete from student_master where RollNo='%s'" % (roll_no)  
db_cursor.execute(Delete)  
db_connection.commit()  
mb.showinfo("Information", "Student Record Deleted Succssfully")  
self.load_student_data()  
self.entFName.delete(0, tk.END)  
self.entLName.delete(0, tk.END)  
self.entContact .delete(0, tk.END)  
self.entCity.delete(0, tk.END)  
self.entState.delete(0, tk.END)  
self.calDOB.delete(0, tk.END)  
def create_table(self):  
if db_connection.is_connected() == False:  
db_connection.connect()  
# executing cursor with execute method and pass SQL query  
db_cursor.execute("CREATE DATABASE IF NOT EXISTS Student") # Create a Database Named Student  
db_cursor.execute("use Student") # Interact with Student Database  
# creating required tables  
db_cursor.execute("create table if not exists Student_master(Id INT(10) NOT NULL PRIMARY KEY AUTO_INCREMENT,rollno INT(15),fname VARCHAR(30),lname VARCHAR(30),city VARCHAR(20),state VARCHAR(30),mobileno VARCHAR(10),dob date)AUTO_INCREMENT=1")  
db_connection.commit()  
def register_student(self):  
if db_connection.is_connected() == False:  
db_connection.connect()  
fname = self.entFName.get() # Retrieving entered first name  
lname = self.entLName.get() # Retrieving entered last name  
contact_no = self.entContact.get() # Retrieving entered contact number  
city = self.entCity.get() # Retrieving entered city name  
state = self.entState.get() # Retrieving entered state name  
dob = self.calDOB.get() # Retrieving choosen date  
# validating Entry Widgets  
if fname == "":  
mb.showinfo('Information', "Please Enter Firstname")  
self.entFName.focus_set()  
return  
if lname == "":  
mb.showinfo('Information', "Please Enter Lastname")  
self.entLName.focus_set()  
return  
if contact_no == "":  
mb.showinfo('Information', "Please Enter Contact Number")  
self.entContact.focus_set()  
return  
if city == "":  
mb.showinfo('Information', "Please Enter City Name")  
self.entCity.focus_set()  
return  
if state == "":  
mb.showinfo('Information', "Please Enter State Name")  
self.entState.focus_set()  
return  
if dob == "":  
mb.showinfo('Information', "Please Choose Date of Birth")  
self.calDOB.focus_set()  
return  
# Inserting record into student_master table of student database  
try:  
rollno =int(self.fetch_max_roll_no())  
print("New Student Id: " + str(rollno))  
query2 = "INSERT INTO student_master (rollno, fname,lname,city,state,mobileno,dob) VALUES (%s, %s,%s, %s,%s, %s, %s)"  
# implement query Sentence  
db_cursor.execute(query2, (rollno, fname, lname, city, state, contact_no,dob))  
mb.showinfo('Information', "Student Registration Successfully")  
# Submit to database for execution  
db_connection.commit()  
self.load_student_data()  
except mysql.connector.Error as err:  
print(err)  
# Rollback in case there is any error  
db_connection.rollback()  
mb.showinfo('Information', "Data insertion failed!!!")  
finally:  
db_connection.close()  
def fetch_max_roll_no(self):  
if db_connection.is_connected() == False:  
db_connection.connect()  
db_cursor.execute("use Student") # Interact with Student Database  
rollno = 0  
query1 = "SELECT rollno FROM student_master order by id DESC LIMIT 1"  
# implement query Sentence  
db_cursor.execute(query1) # Retrieving maximum student id no  
print("No of Record Fetched:" + str(db_cursor.rowcount))  
if db_cursor.rowcount == 0:  
rollno = 1  
else:  
rows = db_cursor.fetchall()  
for row in rows:  
rollno = row[0]  
rollno = rollno + 1  
print("Max Student Id: " + str(rollno))  
return rollno  
def show_search_record(self):  
if db_connection.is_connected() == False:  
db_connection.connect()  
s_roll_no = self.entSearch.get() # Retrieving entered first name  
print(s_roll_no)  
if s_roll_no == "":  
mb.showinfo('Information', "Please Enter Student Roll")  
self.entSearch.focus_set()  
return  
self.tvStudent.delete(*self.tvStudent.get_children()) # clears the treeview tvStudent  
# Inserting record into student_master table of student database  
db_cursor.execute("use Student") # Interact with Bank Database  
sql = "SELECT rollno,fname,lname,city,state,mobileno,date_format(dob,'%d-%m-%Y') FROM student_master where rollno='" + s_roll_no + "'"  
db_cursor.execute(sql)  
total = db_cursor.rowcount  
#if total ==0:  
#mb.showinfo("Info", "Nothing To Display,Please add data")  
#return  
print("Total Data Entries:" + str(total))  
rows = db_cursor.fetchall()  
RollNo = ""  
First_Name = ""  
Last_Name = ""  
City = ""  
State = ""  
Phone_Number = ""  
DOB =""  
for row in rows:  
RollNo = row[0]  
First_Name = row[1]  
Last_Name = row[2]  
City = row[3]  
State = row[4]  
Phone_Number = row[5]  
DOB = row[6]  
print( Phone_Number)  
self.tvStudent.insert("", 'end', text=RollNo, values=(RollNo, First_Name, Last_Name, City, State, Phone_Number,DOB))  
def show_selected_record(self, event):  
self.clear_form()  
for selection in self.tvStudent.selection():  
item = self.tvStudent.item(selection)  
global roll_no  
roll_no,first_name,last_name,city,state,contact_no,dob = item["values"][0:7]  
self.entFName.insert(0, first_name)  
self.entLName.insert(0, last_name)  
self.entCity.insert(0, city)  
self.entState .insert(0, state)  
self.entContact.insert(0, contact_no)  
self.calDOB.insert(0, dob)  
return roll_no  
def update_student_data(self):  
if db_connection.is_connected() == False:  
db_connection.connect()  
print("Updating")  
db_cursor.execute("use Student") # Interact with Student Database  
First_Name = self.entFName.get()  
Last_Name = self.entLName.get()  
Phone_Number = self.entContact.get()  
City = self.entCity.get()  
State = self.entState.get()  
DOB = self.calDOB.get()  
print( roll_no)  
Update = "Update student_master set fname='%s', lname='%s', mobileno='%s', city='%s', state='%s', dob='%s' where rollno='%s'" % (  
First_Name, Last_Name, Phone_Number, City, State,DOB, roll_no)  
db_cursor.execute(Update)  
db_connection.commit()  
mb.showinfo("Info", "Selected Student Record Updated Successfully ")  
self.load_student_data()  
def load_student_data(self):  
if db_connection.is_connected() == False:  
db_connection.connect()  
self.calDOB.delete(0, tk.END)#clears the date entry widget  
self.tvStudent.delete(*self.tvStudent.get_children()) # clears the treeview tvStudent  
# Inserting record into student_master table of student database  
db_cursor.execute("use Student") # Interact with Bank Database  
sql = "SELECT rollno,fname,lname,city,state,mobileno,date_format(dob,'%d-%m-%Y') FROM student_master"  
db_cursor.execute(sql)  
total = db_cursor.rowcount  
#if total ==0:  
#mb.showinfo("Info", "Nothing To Display,Please add data")  
#return  
print("Total Data Entries:" + str(total))  
rows = db_cursor.fetchall()  
RollNo = ""  
First_Name = ""  
Last_Name = ""  
City = ""  
State = ""  
Phone_Number = ""  
DOB =""  
for row in rows:  
RollNo = row[0]  
First_Name = row[1]  
Last_Name = row[2]  
City = row[3]  
State = row[4]  
Phone_Number = row[5]  
DOB = row[6]  
self.tvStudent.insert("", 'end', text=RollNo, values=(RollNo, First_Name, Last_Name, City, State, Phone_Number,DOB))  
if __name__ == "__main__":  
app = StudentApp()  
app.mainloop()   

Originally published at https://www.c-sharpcorner.com/ 

#python #mysql #database

Creating a CRUD Desktop Application using Python 3 and MySQL Database Server