1669709646
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/
1668615180
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.
Before moving out to the methods, let’s take a look at the exit codes that have specific meanings:
Exit Codes | Description |
0 | The script is executed successfully. |
1 | The script is executed with general errors. |
2 | Invalid use of some built-in commands in the script. |
126 | Shows the error for the command which is invoked and cannot be executed. |
127 | The command doesn’t exist in the script. |
128 | Shows the out-of-range exit code or fatal error signal. |
130 | CTRL+C terminates the script. |
255 | A general failure error code of the script. |
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:
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 $?
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/
1661462400
Dockerfile Linter (written in Node.js)
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
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 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.
E
prefix come from dockerfilelinter
. Implementation can be found in lib/lints.js
.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.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
Rules | Description |
---|---|
EL0001 | Invalid line |
ED0001 | All parser directives must be at the very top of a Dockerfile. |
ED0002 | Directive appears more then once. |
ED0003 | Directives should be lowercase. |
ED0004 | Parser directive will be treated as a comment. |
ED0005 | Missing value for directive. |
ER0001 | Set the SHELL option -o (-eo for Alpine image) pipefail before RUN with a pipe in. |
EU0001 | Last user should not be root . |
EI0001 | There can only be one instruction like (CMD, HEALTHCHECK, ENTRYPOINT) . |
EI0002 | FROM may only be preceded by one or more ARG . |
EF0001 | Missing FROM . |
EC0001 | COPY --from cannot reference its own FROM alias. |
EC0002 | COPY --from should reference a previously defined FROM alias. |
EI0003 | MAINTAINER is deprecated, instead use LABEL . |
EJ0001 | You must use double-quotes (") in JSON array. |
EJ0002 | CMD and ENTRYPOINT should be written in JSON form. |
EJ0003 | SHELL must be written in JSON form. |
EF0002 | FROM aliases must be unique. |
EF0003 | Using latest is prone to errors if the image will ever update. |
EF0004 | Always tag the version of an image explicitly. |
ER0002 | Delete the apt-get lists after installing something. |
ER0003 | Use WORKDIR to switch to a directory. |
ER0004 | Do not use sudo, consider using gosu. |
ER0005 | Command (ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig) does not make sense in a container. |
ER0006 | Using (apt-get upgrade, dist-upgrade, apk upgrade, apt install) is not recommended. |
EA0001 | Use curl or wget instead, and delete files when no longer needed. |
EC0003 | Use ADD for extracting archives into a image. |
ER0007 | Either use wget or curl , but not both. |
ER0008 | Use SHELL to change the default shell. |
ER0009 | Use the -y switch. |
ER0010 | Avoid additional packages by specifying --no-install-recommends . |
EA0002 | Use COPY instead of ADD for files and folders. |
EC0004 | COPY with more then 2 arguments requires the last argument to end with / . |
ER0011 | Use the --no-cache switch. |
ER0012 | Pin versions in apt get install . |
ER0013 | Pin versions in pip install . |
ER0014 | Pin versions in npm install . |
ER0015 | Pin versions in apk add . |
ER0016 | Pin versions in gem install . |
EI0004 | Don't use (ONBUILD,FROM,MAINTAINTER) in ONBUILD . |
EW0001 | Use absolute WORKDIR . |
EE0001 | Valid UNIX ports range from 0 to 65535. |
EI0005 | Instructions should be uppercase. |
Development
You can help us develop linter by suggesting new rules and reporting bugs.
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:
Lint Dockerfile
action and select the Dockerfile to validate:Download Details:
Author: buddy-works
Source code: https://github.com/buddy-works/dockerfile-linter
License: GPL-3.0 license
#docker #nodejs
1659736920
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.
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
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
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.
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.
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
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.
#alfa wants to send a message to beta
alfa.send_message(beta, "Body", "subject")
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
#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")
#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)
#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
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.
Thanks to Roman Kushnir (@RKushnir) you can test Mailboxer with this sample app.
If you need a GUI you should take a look at these links:
Author: mailboxer
Source code: https://github.com/mailboxer/mailboxer
License: MIT license
1654310400
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-request
biblioteca para realizar operaciones de red en nuestro backend.
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-request
entra 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:
graphql-request
es 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-express
paquete para construir un servidor backend. Además, para la interfaz, usaremos Next y graphql-request
consumiremos nuestra API GraphQL.
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 app
carpeta. Aquí, crea los siguientes archivos:
index.ts
: Nuestro archivo principal. Esto ejecutará y ejecutará nuestro servidor Express GraphQLdataset.ts
: Esto servirá como nuestra base de datos, que se servirá al clienteResolvers.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 clienteAl final, la estructura de carpetas debería verse así:
En esta sección, crearemos una base de datos ficticia que se utilizará para enviar los datos solicitados. Para hacerlo, vaya a app/dataset.ts
y 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;
people
id
de tipo number
y name
de tipostring
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.ts
archivo. 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:
Schema
variable contiene nuestro esquema GraphQLPerson
esquema. Tendrá dos campos: id
de tipo ID
y name
de tipoString
getAllPeople
comando, el servidor devolverá una matriz de Person
objetosgetPerson
comando, GraphQL devolverá una sola Person
instanciaAhora 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;
Query
objeto que maneja todas las consultas entrantes que van al servidorgetAllPeople
comando, el programa devolverá todos los objetos presentes en nuestra base de datosgetPerson
comando requiere un argumento id
. Esto devolverá una Person
instancia con el ID coincidente¡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/graphql
URL.
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 getPerson
consulta:
query ($getPersonId: Int) { #the argument will be of type Integer
getPerson(id: 1) {
#get the person with the ID of 1
name
id
}
}
En el mundo de GraphQL, las mutaciones son comandos que tienen efectos secundarios en la base de datos. Ejemplos comunes de esto incluyen:
Para manejar mutaciones, vaya a su Schema.ts
módulo. Aquí, dentro de la Schema
variable, 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.ts
archivo, 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
},
},
};
addPerson
mutación acepta un name
argumento.name
se pasa a, el programa creará un nuevo objeto con una name
clave coincidentepush
método para agregar este objeto al conjunto de people
datos .¡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
}
}
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
En esta sección, codificaremos nuestras consultas y mutaciones para ayudarnos a realizar operaciones GraphQL. Para hacerlo, vaya a constants.tsx
y 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 };
getAllPeopleQuery
variable. Cuando el usuario ejecuta esta consulta, el programa le indicará al servidor que obtenga todas las entradas presentes en la base de datos.addPerson
mutación le dice a GraphQL que agregue una nueva entrada con su name
campo respectivoexport
palabra clave para vincular nuestras variables con el resto del proyecto.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:
getStaticProps
método, le indicamos a Next que ejecute el getAllPeople
comando en nuestro servidor GraphQLHome
componente funcional. Esto significa que ahora podemos mostrar el resultado en la interfaz de usuario.map
método para representar todos los resultados del getAllPeople
comando en la interfaz de usuario. Cada elemento de párrafo mostrará los name
campos de cada entradaLink
componente para redirigir al usuario a la addpage
ruta. Esto permitirá al usuario agregar una nueva Person
instancia 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.
Ahora que hemos realizado con éxito una consulta, incluso podemos realizar mutaciones a través de la graphql-request
biblioteca.
Dentro de su pages
carpeta, 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.tsx
archivo:
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 onSubmit
controlador ubicado en pages/addpage.tsx
el archivo de esta manera:
const onSubmit = async (data: any) => {
const response = await request(
"http://localhost:4000/graphql",
addPersonMutation,
data
);
console.log(response);
};
request
funciónaddPerson
comando de mutación a nuestro encabezado de solicitud. Esto le indicará a GraphQL que realice la addMutation
acción en nuestro servidorEste será el resultado:
¡Y hemos terminado!
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/
1654310040
この記事では、バックエンドでGraphQLとNode.jsを使用してフルスタックアプリを構築します。一方、フロントエンドはgraphql-request
ライブラリを使用してバックエンドでネットワーク操作を実行します。
開発者がApolloを使用してGraphQLサーバーを構築するときはいつでも、ライブラリは次のような「フロントエンド」を生成します。
このインターフェースにより、ユーザーはコードを介してサーバーにクエリまたはミューテーション要求を行うことができます。ただし、部屋の中の象について説明しましょう。あまりユーザーフレンドリーではないようです。フロントエンドにはボタンや便利なインターフェース要素がないため、多くのユーザーがアプリ内を移動するのは難しいかもしれません。その結果、これによりユーザーベースが縮小します。では、この問題をどのように解決するのでしょうか。
これがgraphql-request
出番です。これは、ユーザーがGraphQLサーバーでクエリを実行できるようにするオープンソースライブラリです。次の機能を備えています。
graphql-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つずつ分解してみましょう。
Schema
GraphQLスキーマが含まれていますPerson
スキーマを作成しました。id
タイプID
とname
タイプの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の世界では、ミューテーションはデータベースに副作用をもたらすコマンドです。この一般的な例は次のとおりです。
ミューテーションを処理するには、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
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;
このコードの内訳は次のとおりです。
getStaticProps
を実行するようにNextに指示しましたgetAllPeople
Home
その後、機能コンポーネントへの応答を返しました。これは、結果をUIにレンダリングできることを意味しますmap
にレンダリングしました。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サーバーへのミューテーションリクエストを実行していますaddPerson
mutationコマンドをリクエストヘッダーにも渡しました。これにより、GraphQLにaddMutation
サーバーでアクションを実行するように指示されますこれが結果になります:
これで完了です。
この記事では、GraphQLとTypeScriptを使用してフルスタックアプリを作成する方法を学びました。これらは両方とも、今日需要が高いため、プログラミングの世界では非常に重要なスキルです。
このコードで問題が発生した場合は、この概念を完全に理解できるように、コードを分解して試してみることをお勧めします。
読んでくれてありがとう!ハッピーコーディング!
このストーリーは、もともとhttps://blog.logrocket.com/build-graphql-app-node-js-typescript-graphql-request/で公開されました
1646698200
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.
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.
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.
cv2.face.createEigenFaceRecognizer()
cv2.face.createFisherFaceRecognizer()
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.
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 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.
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 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.
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
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
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 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.
[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.
Before starting the actual coding we need to import the required modules for coding. So let's import them first.
#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
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"]
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.
s1, s2
.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.[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!
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.
As we know, OpenCV comes equipped with three face recognizers.
cv2.face.createEigenFaceRecognizer()
cv2.face.createFisherFaceRecognizer()
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!
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
predict(face)
method. This method will return a lableNow 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!
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
1645849980
eradicate
eradicate
removes commented-out code from Python files.
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.)
$ 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
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
.
There are different tools, plugins, and integrations for eradicate
users:
Author: Myint
Source Code: https://github.com/myint/eradicate
License:
1597823927
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.
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